Example #1
0
    def save_sampling_classification(self, file_out):
        crs = self.sampling_layer.crs().toWkt()
        # create layer
        vlayer = QgsVectorLayer("Point?crs=" + crs, "temporary_points", "memory")
        pr = vlayer.dataProvider()
        # add fields
        if self.with_thematic_classes:
            pr.addAttributes([QgsField("ID", QVariant.Int),
                              QgsField("Class Name", QVariant.String),
                              QgsField("Classified", QVariant.Int),
                              QgsField("Thematic Class", QVariant.Int),
                              QgsField("Match", QVariant.String)])
        else:
            pr.addAttributes([QgsField("ID", QVariant.Int),
                              QgsField("Class Name", QVariant.String),
                              QgsField("Classif ID", QVariant.Int)])
        vlayer.updateFields()  # tell the vector layer to fetch changes from the provider

        if self.with_thematic_classes:
            from AcATaMa.gui.acatama_dockwidget import AcATaMaDockWidget as AcATaMa
            ThematicR = Raster(file_selected_combo_box=AcATaMa.dockwidget.QCBox_ThematicRaster,
                               band=int(AcATaMa.dockwidget.QCBox_band_ThematicRaster.currentText()),
                               nodata=int(AcATaMa.dockwidget.nodata_ThematicRaster.value()))

        points_ordered = sorted(self.points, key=lambda p: p.shape_id)
        for point in points_ordered:
            # add a feature
            feature = QgsFeature()
            feature.setGeometry(point.QgsGeom)
            name = self.buttons_config[point.classif_id]["name"] if point.is_classified else NULL
            if self.with_thematic_classes:
                classified = int(
                    self.buttons_config[point.classif_id]["thematic_class"]) if point.is_classified else NULL
                thematic = int(ThematicR.get_pixel_value_from_pnt(point.QgsPnt)) \
                    if point.is_classified and ThematicR.get_pixel_value_from_pnt(point.QgsPnt) else NULL
                match = ('Yes' if thematic == classified else 'No') if point.is_classified else NULL
                feature.setAttributes([point.shape_id, name, classified, thematic, match])
            else:
                feature.setAttributes([point.shape_id, name, point.classif_id])
            pr.addFeatures([feature])

        vlayer.commitChanges()
        vlayer.updateExtents()

        file_format = \
            "GPKG" if file_out.endswith(".gpkg") else "ESRI Shapefile" if file_out.endswith(".shp") else None
        QgsVectorFileWriter.writeAsVectorFormat(vlayer, file_out, "System", self.sampling_layer.crs(), file_format)
Example #2
0
    def __init__(self, classification):
        from AcATaMa.gui.acatama_dockwidget import AcATaMaDockWidget as AcATaMa

        self.classification = classification
        self.ThematicR = Raster(file_selected_combo_box=AcATaMa.dockwidget.QCBox_ThematicRaster,
                                band=int(AcATaMa.dockwidget.QCBox_band_ThematicRaster.currentText())
                                    if AcATaMa.dockwidget.QCBox_band_ThematicRaster.currentText() else None,
                                nodata=int(AcATaMa.dockwidget.nodata_ThematicRaster.value()))
        self.thematic_pixels_count = {}
        # dialog settings
        self.area_unit = None
        self.z_score = 1.96
        self.csv_separator = ";"
        self.csv_decimal = "."
        # define the base area unit based on the thematic raster distance unit
        thematic_layer = AcATaMa.dockwidget.QCBox_ThematicRaster.currentLayer()
        layer_dist_unit = thematic_layer.crs().mapUnits()
        self.base_area_unit = QgsUnitTypes.distanceToAreaUnit(layer_dist_unit)
Example #3
0
class AccuracyAssessment(object):
    def __init__(self, classification):
        from AcATaMa.gui.acatama_dockwidget import AcATaMaDockWidget as AcATaMa

        self.classification = classification
        self.ThematicR = Raster(
            file_selected_combo_box=AcATaMa.dockwidget.QCBox_ThematicRaster,
            band=int(
                AcATaMa.dockwidget.QCBox_band_ThematicRaster.currentText())
            if AcATaMa.dockwidget.QCBox_band_ThematicRaster.currentText() else
            None,
            nodata=int(AcATaMa.dockwidget.nodata_ThematicRaster.value()))
        self.thematic_pixels_count = {}
        # dialog settings
        self.area_unit = None
        self.z_score = 1.96
        self.csv_separator = ";"
        self.csv_decimal = "."
        # define the base area unit based on the thematic raster distance unit
        self.dist_unit = self.ThematicR.qgs_layer.crs().mapUnits()
        if self.dist_unit == QgsUnitTypes.DistanceUnknownUnit:
            # thematic raster with unknown map unit
            self.dist_unit = QgsUnitTypes.DistanceMeters
        self.base_area_unit = QgsUnitTypes.distanceToAreaUnit(self.dist_unit)

    @wait_process
    def compute(self):
        from AcATaMa.gui.acatama_dockwidget import AcATaMaDockWidget as AcATaMa
        AcATaMa.dockwidget.QPBtn_ComputeTheAccurasyAssessment.setText(
            "Processing, please wait ...")
        QApplication.processEvents()

        # get labels from classification buttons
        labels = {}
        for button_config in self.classification.buttons_config.values():
            labels[button_config["thematic_class"]] = button_config["name"]

        # get the classified and thematic map values
        thematic_map_values = []
        classified_values = []
        samples_outside_the_thematic = []
        classification_points = [
            point for point in self.classification.points
            if point.is_classified
        ]
        points_ordered = sorted(classification_points,
                                key=lambda p: p.shape_id)
        for point in points_ordered:
            # classification from the pixel values in the thematic map
            thematic_map_value = self.ThematicR.get_pixel_value_from_pnt(
                point.QgsPnt)
            if not thematic_map_value:
                samples_outside_the_thematic.append(point)
                continue
            thematic_map_values.append(int(thematic_map_value))
            # classified value made/checked by user with classification buttons
            classified_value = self.classification.buttons_config[
                point.classif_id]["thematic_class"]
            classified_values.append(int(classified_value))

        # all unique and sorted values
        values = sorted(set(thematic_map_values + classified_values))
        # Construct a value->index dictionary
        indices = dict((val, i) for (i, val) in enumerate(values))

        # calculate the error/confusion matrix
        # https://github.com/nltk/nltk/blob/develop/nltk/metrics/confusionmatrix.py
        #
        #             classified
        #   t |    | L1 | L2 | L3 | L4 |
        #   h | L1 |    |    |    |    |
        #   e | L2 |    |    |    |    |
        #   m | L3 |    |    |    |    |
        #   a | L4 |    |    |    |    |
        #
        error_matrix = [[0 for column in values] for row in values]
        for thematic, classified in zip(thematic_map_values,
                                        classified_values):
            error_matrix[indices[thematic]][indices[classified]] += 1

        # calculate the total number of pixel in the thematic raster
        # by each thematic raster class used in the classification buttons
        for thematic_map_value in values:
            if thematic_map_value not in self.thematic_pixels_count:
                self.thematic_pixels_count[
                    thematic_map_value] = self.ThematicR.get_total_pixels_by_value(
                        thematic_map_value)

        # values necessary for results
        self.values = values
        self.labels = labels
        self.error_matrix = error_matrix
        self.samples_outside_the_thematic = samples_outside_the_thematic
        # set area by pixel
        self.pixel_area_base = self.ThematicR.qgs_layer.rasterUnitsPerPixelX(
        ) * self.ThematicR.qgs_layer.rasterUnitsPerPixelY()
        self.pixel_area_value = self.pixel_area_base * QgsUnitTypes.fromUnitToUnitFactor(
            self.base_area_unit, self.area_unit)
        self.pixel_area_unit = QgsUnitTypes.toAbbreviatedString(self.area_unit)
Example #4
0
def do_simple_random_sampling(dockwidget):
    # first check input files requirements
    if not valid_file_selected_in(dockwidget.QCBox_ThematicRaster,
                                  "thematic raster"):
        return
    if dockwidget.QGBox_SimpRSwithCR.isChecked():
        if not valid_file_selected_in(dockwidget.QCBox_CategRaster_SimpRS,
                                      "categorical raster"):
            return
    # get and define some variables
    number_of_samples = int(dockwidget.numberOfSamples_SimpRS.value())
    min_distance = float(dockwidget.minDistance_SimpRS.value())

    ThematicR = Raster(file_selected_combo_box=dockwidget.QCBox_ThematicRaster,
                       band=int(
                           dockwidget.QCBox_band_ThematicRaster.currentText()),
                       nodata=int(dockwidget.nodata_ThematicRaster.value()))

    # simple random sampling in categorical raster
    if dockwidget.QGBox_SimpRSwithCR.isChecked():
        CategoricalR = Raster(
            file_selected_combo_box=dockwidget.QCBox_CategRaster_SimpRS,
            band=int(dockwidget.QCBox_band_CategRaster_SimpRS.currentText()))
        try:
            pixel_values = [
                int(p)
                for p in dockwidget.pixelsValuesCategRaster.text().split(",")
            ]
        except:
            iface.messageBar().pushMessage(
                "AcATaMa",
                "Error, wrong pixel values, set only integers and separated by commas",
                level=Qgis.Warning)
            return
    else:
        CategoricalR = None
        pixel_values = None

    # check neighbors aggregation
    if dockwidget.widget_generate_SimpRS.QGBox_neighbour_aggregation.isChecked(
    ):
        number_of_neighbors = int(dockwidget.widget_generate_SimpRS.
                                  QCBox_NumberOfNeighbors.currentText())
        same_class_of_neighbors = int(dockwidget.widget_generate_SimpRS.
                                      QCBox_SameClassOfNeighbors.currentText())
        neighbor_aggregation = (number_of_neighbors, same_class_of_neighbors)
    else:
        neighbor_aggregation = None

    # set the attempts_by_sampling
    if dockwidget.widget_generate_SimpRS.button_attempts_by_sampling.isChecked(
    ):
        attempts_by_sampling = int(
            dockwidget.widget_generate_SimpRS.attempts_by_sampling.value())
    else:
        attempts_by_sampling = None

    # first select the target dir for save the sampling file
    suggested_filename = os.path.join(os.path.dirname(ThematicR.file_path),
                                      "random_sampling.gpkg")
    output_file, _ = QFileDialog.getSaveFileName(
        dockwidget,
        dockwidget.tr("Select the output file to save the sampling"),
        suggested_filename,
        dockwidget.tr(
            "GeoPackage files (*.gpkg);;Shape files (*.shp);;All files (*.*)"))
    if output_file == '':
        return

    # define the random seed
    if dockwidget.widget_generate_SimpRS.button_random_seed_by_user.isChecked(
    ):
        random_seed = dockwidget.widget_generate_SimpRS.random_seed_by_user.text(
        )
        try:
            random_seed = int(random_seed)
        except:
            pass
    else:
        random_seed = None

    # process
    sampling = Sampling("simple",
                        ThematicR,
                        CategoricalR,
                        output_file=output_file)
    sampling.generate_sampling_points(
        pixel_values, number_of_samples, min_distance, neighbor_aggregation,
        attempts_by_sampling,
        dockwidget.widget_generate_SimpRS.QPBar_GenerateSampling, random_seed)

    # success
    if sampling.total_of_samples == number_of_samples:
        load_layer(sampling.output_file)
        iface.messageBar().pushMessage(
            "AcATaMa",
            "Generate the simple random sampling, completed",
            level=Qgis.Success)
    # success but not completed
    if number_of_samples > sampling.total_of_samples > 0:
        load_layer(sampling.output_file)
        iface.messageBar().pushMessage(
            "AcATaMa",
            "Generated the simple random sampling, but can not generate requested number of "
            "random points {}/{}, attempts exceeded".format(
                sampling.total_of_samples, number_of_samples),
            level=Qgis.Warning,
            duration=-1)
    # zero points
    if sampling.total_of_samples < number_of_samples and sampling.total_of_samples == 0:
        # delete instance where storage all sampling generated
        Sampling.samplings.pop(sampling.filename, None)
        iface.messageBar().pushMessage(
            "AcATaMa",
            "Error, could not generate any random points with this settings, "
            "attempts exceeded",
            level=Qgis.Warning,
            duration=-1)
Example #5
0
def do_stratified_random_sampling(dockwidget):
    # first check input files requirements
    if not valid_file_selected_in(dockwidget.QCBox_ThematicRaster,
                                  "thematic raster"):
        return
    if not valid_file_selected_in(dockwidget.QCBox_CategRaster_StraRS,
                                  "categorical raster"):
        return
    # get and define some variables
    min_distance = float(dockwidget.minDistance_StraRS.value())
    ThematicR = Raster(file_selected_combo_box=dockwidget.QCBox_ThematicRaster,
                       band=int(
                           dockwidget.QCBox_band_ThematicRaster.currentText()),
                       nodata=int(dockwidget.nodata_ThematicRaster.value()))
    CategoricalR = Raster(
        file_selected_combo_box=dockwidget.QCBox_CategRaster_StraRS,
        band=int(dockwidget.QCBox_band_CategRaster_StraRS.currentText()),
        nodata=int(dockwidget.nodata_CategRaster_StraRS.value()))

    # get values from category table  #########
    pixel_values = []
    number_of_samples = []
    for row in range(dockwidget.QTableW_StraRS.rowCount()):
        pixel_values.append(int(dockwidget.QTableW_StraRS.item(row, 0).text()))
        number_of_samples.append(dockwidget.QTableW_StraRS.item(row, 2).text())
    # convert and check if number of samples only positive integers
    try:
        number_of_samples = [int(ns) for ns in number_of_samples]
        if True in [ns < 0 for ns in number_of_samples]:
            raise Exception
    except:
        iface.messageBar().pushMessage(
            "AcATaMa",
            "Error, the number of samples should be only positive integers",
            level=Qgis.Warning)
        return
    total_of_samples = sum(number_of_samples)
    if total_of_samples == 0:
        iface.messageBar().pushMessage(
            "AcATaMa",
            "Error, no number of samples configured!",
            level=Qgis.Warning)
        return

    # check neighbors aggregation
    if dockwidget.widget_generate_StraRS.QGBox_neighbour_aggregation.isChecked(
    ):
        number_of_neighbors = int(dockwidget.widget_generate_StraRS.
                                  QCBox_NumberOfNeighbors.currentText())
        same_class_of_neighbors = int(dockwidget.widget_generate_StraRS.
                                      QCBox_SameClassOfNeighbors.currentText())
        neighbor_aggregation = (number_of_neighbors, same_class_of_neighbors)
    else:
        neighbor_aggregation = None

    # set the attempts_by_sampling
    if dockwidget.widget_generate_StraRS.button_attempts_by_sampling.isChecked(
    ):
        attempts_by_sampling = int(
            dockwidget.widget_generate_StraRS.attempts_by_sampling.value())
    else:
        attempts_by_sampling = None

    # set the method of stratified sampling and save StraRS config
    if dockwidget.QCBox_StraRS_Method.currentText().startswith("Fixed values"):
        sampling_method = "fixed values"
        srs_config = None
    if dockwidget.QCBox_StraRS_Method.currentText().startswith(
            "Area based proportion"):
        sampling_method = "area based proportion"
        srs_config = {}
        # save total expected std error
        srs_config["total_std_error"] = dockwidget.TotalExpectedSE.value()
        # get std_dev from table
        srs_config["std_dev"] = []
        for row in range(dockwidget.QTableW_StraRS.rowCount()):
            srs_config["std_dev"].append(
                float(dockwidget.QTableW_StraRS.item(row, 3).text()))

    # first select the target dir for save the sampling file
    suggested_filename = os.path.join(os.path.dirname(ThematicR.file_path),
                                      "stratified_random_sampling.gpkg")
    output_file, _ = QFileDialog.getSaveFileName(
        dockwidget,
        dockwidget.tr("Select the output file to save the sampling"),
        suggested_filename,
        dockwidget.tr(
            "GeoPackage files (*.gpkg);;Shape files (*.shp);;All files (*.*)"))
    if output_file == '':
        return

    # define the random seed
    if dockwidget.widget_generate_StraRS.button_random_seed_by_user.isChecked(
    ):
        random_seed = dockwidget.widget_generate_StraRS.random_seed_by_user.text(
        )
        try:
            random_seed = int(random_seed)
        except:
            pass
    else:
        random_seed = None

    # process
    sampling = Sampling("stratified",
                        ThematicR,
                        CategoricalR,
                        sampling_method,
                        srs_config=srs_config,
                        output_file=output_file)
    sampling.generate_sampling_points(
        pixel_values, number_of_samples, min_distance, neighbor_aggregation,
        attempts_by_sampling,
        dockwidget.widget_generate_StraRS.QPBar_GenerateSampling, random_seed)

    # success
    if sampling.total_of_samples == total_of_samples:
        load_layer(sampling.output_file)
        iface.messageBar().pushMessage(
            "AcATaMa",
            "Generate the stratified random sampling, completed",
            level=Qgis.Success)
    # success but not completed
    if sampling.total_of_samples < total_of_samples and sampling.total_of_samples > 0:
        load_layer(sampling.output_file)
        iface.messageBar().pushMessage(
            "AcATaMa",
            "Generated the stratified random sampling, but can not generate requested number of "
            "random points {}/{}, attempts exceeded".format(
                sampling.total_of_samples, total_of_samples),
            level=Qgis.Warning,
            duration=-1)
    # zero points
    if sampling.total_of_samples < total_of_samples and sampling.total_of_samples == 0:
        # delete instance where storage all sampling generated
        Sampling.samplings.pop(sampling.filename, None)
        iface.messageBar().pushMessage(
            "AcATaMa",
            "Error, could not generate any stratified random points with this settings, "
            "attempts exceeded",
            level=Qgis.Warning,
            duration=-1)