Esempio n. 1
0
    def change_cell_value(self, vals, x=None, y=None):
        """Save new bands values to data provider"""

        if not self.rdp.isEditable():
            success = self.rdp.setEditable(True)
            if not success:
                self.uc.show_warn('QGIS can\'t modify this type of raster')
                return

        if not x:
            x = self.px
            y = self.py

        for nr in range(1, min(4, self.band_count + 1)):
            rblock = QgsRasterBlock(self.bands[nr]['qtype'], 1, 1)
            rblock.setValue(0, 0, vals[nr - 1])
            success = self.rdp.writeBlock(rblock, nr, x, y)
            if not success:
                self.uc.show_warn('QGIS can\'t modify this type of raster')
                return

        self.rdp.setEditable(False)
        self.raster.triggerRepaint()

        # prepare raster for next actions
        self.prepare_raster(True)
        self.check_undo_redo_btns()
Esempio n. 2
0
    def edit_pixel(self, point, new_value=None, check_bounds=True, inside_geom=None):
        if check_bounds and not self.check_point_inside_layer(point):
            return

        if new_value is None:
            old_value, new_value = self.get_the_old_new_pixel_value(point)
            if new_value is None:
                return
        else:
            old_value = self.get_pixel_value_from_pnt(point)

        if inside_geom and not inside_geom.contains(QgsGeometry.fromPointXY(point)):
            return

        px = int((point.x() - self.bounds[0]) / self.qgs_layer.rasterUnitsPerPixelX())  # num column position in x
        py = int((self.bounds[3] - point.y()) / self.qgs_layer.rasterUnitsPerPixelY())  # num row position in y

        rblock = QgsRasterBlock(self.data_provider.dataType(self.band), 1, 1)
        rblock.setValue(0, 0, new_value)
        if self.data_provider.writeBlock(rblock, self.band, px, py):  # write and check if writing status is ok
            return point, old_value
Esempio n. 3
0
    def processAlgorithm(self, parameters, context, feedback):
        inp_rast = self.parameterAsRasterLayer(parameters, self.INPUT, context)

        grib_filename = self.parameterAsString(parameters, self.OUTPUT,
                                               context)
        if not grib_filename:
            raise QgsProcessingException(
                self.tr('You need to specify output filename.'))

        idp = inp_rast.dataProvider()
        map_settings = iface.mapCanvas().mapSettings()
        crs = map_settings.destinationCrs()
        outputFormat = QgsRasterFileWriter.driverForExtension('.tif')

        height = inp_rast.height()
        width = inp_rast.width()
        inp_block = idp.block(1, idp.extent(), width, height)

        rfw = QgsRasterFileWriter(grib_filename + '.tif')
        rfw.setOutputProviderKey('gdal')
        rfw.setOutputFormat(outputFormat)
        rdp = rfw.createMultiBandRaster(Qgis.Float32, width, height,
                                        idp.extent(), crs, 2)

        rdp.setEditable(True)
        x_block = QgsRasterBlock(Qgis.Float32, width, height)
        y_block = QgsRasterBlock(Qgis.Float32, width, height)
        diag = 1. / sqrt(2)

        # resulting raster has no NODATA value set, which
        # is not treated correctly in MDAL 0.2.0. See
        # see https://github.com/lutraconsulting/MDAL/issues/104
        # therefore set some small value to overcome the issue
        dir_map = {
            0: (1e-7, 1),
            1: (diag, diag),
            2: (1, 1e-7),
            3: (diag, -diag),
            4: (1e-7, -1),
            5: (-diag, -diag),
            6: (-1, 1e-7),
            7: (-diag, diag),
            255: (0, 0)
        }
        for row in range(height):
            for col in range(width):
                dir_ind = inp_block.value(row, col)
                try:
                    x_val, y_val = dir_map.get(dir_ind, 255)
                except TypeError:
                    x_val, y_val = (0, 0)
                x_block.setValue(row, col, x_val)
                y_block.setValue(row, col, y_val)

        rdp.writeBlock(x_block, 1)
        rdp.writeBlock(y_block, 2)
        rdp.setNoDataValue(1, inp_block.noDataValue())
        rdp.setNoDataValue(2, inp_block.noDataValue())
        rdp.setEditable(False)

        # rewrite the resulting raster as GRIB using GDAL for setting metadata
        res_tif = gdal.Open(grib_filename + '.tif')
        grib_driver = gdal.GetDriverByName('GRIB')
        grib = grib_driver.CreateCopy(grib_filename, res_tif)
        band_names = ('x-flow', 'y-flow')
        for i in range(2):
            band_nr = i + 1
            band_name = band_names[i]
            grib_band = grib.GetRasterBand(band_nr)
            grib_band.SetMetadataItem('grib_comment', band_name)
            grib_band.SetNoDataValue(255)
            grib_band.SetDescription(band_name)
            res_tif_band_array = res_tif.GetRasterBand(band_nr).ReadAsArray()
            grib_band.WriteArray(res_tif_band_array)
            feedback.setProgress(band_nr * 50)
        grib = None
        return {self.OUTPUT: grib_filename}
    def processAlgorithm(self, parameters, context, feedback):
        inp_rast = self.parameterAsRasterLayer(parameters, self.INPUT, context)

        grib_filename = self.parameterAsString(parameters, self.OUTPUT, context)
        if not grib_filename:
            raise QgsProcessingException(self.tr('You need to specify output filename.'))

        idp = inp_rast.dataProvider()
        map_settings = iface.mapCanvas().mapSettings()
        crs = map_settings.destinationCrs()
        outputFormat = QgsRasterFileWriter.driverForExtension('.tif')

        height = inp_rast.height()
        width = inp_rast.width()
        inp_block = idp.block(1, idp.extent(), width, height)

        rfw = QgsRasterFileWriter(grib_filename + '.tif')
        rfw.setOutputProviderKey('gdal')
        rfw.setOutputFormat(outputFormat)
        rdp = rfw.createMultiBandRaster(
            Qgis.Float32,
            width,
            height,
            idp.extent(),
            crs,
            2
        )

        rdp.setEditable(True)
        x_block = QgsRasterBlock(Qgis.Float32, width, height)
        y_block = QgsRasterBlock(Qgis.Float32, width, height)
        diag = 1. / sqrt(2)

        # resulting raster has no NODATA value set, which
        # is not treated correctly in MDAL 0.2.0. See
        # see https://github.com/lutraconsulting/MDAL/issues/104
        # therefore set some small value to overcome the issue
        dir_map = {
            0: (1e-7, 1),
            1: (diag, diag),
            2: (1, 1e-7),
            3: (diag, -diag),
            4: (1e-7, -1),
            5: (-diag, -diag),
            6: (-1, 1e-7),
            7: (-diag, diag),
            255: (0, 0)
        }
        for row in range(height):
            for col in range(width):
                dir_ind = inp_block.value(row, col)
                try:
                    x_val, y_val = dir_map.get(dir_ind, 255)
                except TypeError:
                    x_val, y_val = (0, 0)
                x_block.setValue(row, col, x_val)
                y_block.setValue(row, col, y_val)

        rdp.writeBlock(x_block, 1)
        rdp.writeBlock(y_block, 2)
        rdp.setNoDataValue(1, inp_block.noDataValue())
        rdp.setNoDataValue(2, inp_block.noDataValue())
        rdp.setEditable(False)

        # rewrite the resulting raster as GRIB using GDAL for setting metadata
        res_tif = gdal.Open(grib_filename + '.tif')
        grib_driver = gdal.GetDriverByName('GRIB')
        grib = grib_driver.CreateCopy(grib_filename, res_tif)
        band_names = ('x-flow', 'y-flow')
        for i in range(2):
            band_nr = i + 1
            band_name = band_names[i]
            grib_band = grib.GetRasterBand(band_nr)
            grib_band.SetMetadataItem('grib_comment', band_name)
            grib_band.SetNoDataValue(255)
            grib_band.SetDescription(band_name)
            res_tif_band_array = res_tif.GetRasterBand(band_nr).ReadAsArray()
            grib_band.WriteArray(res_tif_band_array)
            feedback.setProgress(band_nr * 50)
        grib = None
        return {self.OUTPUT: grib_filename}
    def processAlgorithm(self, parameters, context, feedback):
        """
        Here is where the processing itself takes place.
        """

        if self.parameterAsRasterLayer(parameters, self.INITIAL_ROAD_NETWORK,
                                       context):
            initial_road_network_raster = self.parameterAsRasterLayer(
                parameters, self.INITIAL_ROAD_NETWORK, context)
        else:
            initial_road_network_raster = None

        basic_distance_cost = self.parameterAsDouble(parameters,
                                                     self.BASIC_DISTANCE_COST,
                                                     context)

        if self.parameterAsRasterLayer(parameters,
                                       self.COARSE_ELEVATION_RASTER, context):
            coarse_elevation_raster = self.parameterAsRasterLayer(
                parameters, self.COARSE_ELEVATION_RASTER, context)
        else:
            coarse_elevation_raster = None

        if self.parameterAsMatrix(parameters, self.COARSE_ELEVATION_COSTS,
                                  context):
            coarse_elevation_costs = self.parameterAsMatrix(
                parameters, self.COARSE_ELEVATION_COSTS, context)
            coarse_elevation_costs = CostRasterCreatorHelper.CollapsedTableToMatrix(
                coarse_elevation_costs, 3)
        else:
            coarse_elevation_costs = None

        if self.parameterAsRasterLayer(parameters, self.FINE_ELEVATION_RASTER,
                                       context):
            fine_elevation_raster = self.parameterAsRasterLayer(
                parameters, self.FINE_ELEVATION_RASTER, context)
        else:
            fine_elevation_raster = None

        if self.parameterAsMatrix(parameters, self.FINE_ELEVATION_COSTS,
                                  context):
            fine_elevation_costs = self.parameterAsMatrix(
                parameters, self.FINE_ELEVATION_COSTS, context)
            fine_elevation_costs = CostRasterCreatorHelper.CollapsedTableToMatrix(
                fine_elevation_costs, 3)
        else:
            fine_elevation_costs = None

        if self.parameterAsRasterLayer(parameters, self.COARSE_WATER_RASTER,
                                       context):
            coarse_water_raster = self.parameterAsRasterLayer(
                parameters, self.COARSE_WATER_RASTER, context)
        else:
            coarse_water_raster = None

        if self.parameterAsDouble(parameters, self.COARSE_WATER_COST, context):
            coarse_water_cost = self.parameterAsDouble(parameters,
                                                       self.COARSE_WATER_COST,
                                                       context)
        else:
            coarse_water_cost = None

        if self.parameterAsRasterLayer(parameters, self.FINE_WATER_RASTER,
                                       context):
            fine_water_raster = self.parameterAsRasterLayer(
                parameters, self.FINE_WATER_RASTER, context)
        else:
            fine_water_raster = None

        if self.parameterAsDouble(parameters, self.FINE_WATER_COST, context):
            fine_water_cost = self.parameterAsDouble(parameters,
                                                     self.FINE_WATER_COST,
                                                     context)
        else:
            fine_water_cost = None

        if self.parameterAsRasterLayer(parameters, self.SOIL_RASTER, context):
            soil_raster = self.parameterAsRasterLayer(parameters,
                                                      self.SOIL_RASTER,
                                                      context)
        else:
            soil_raster = None

        if self.parameterAsRasterLayer(parameters, self.ADDITIONAL_COST_RASTER,
                                       context):
            special_raster = self.parameterAsRasterLayer(
                parameters, self.ADDITIONAL_COST_RASTER, context)
        else:
            special_raster = None

        feedback.pushInfo(self.tr("Checking inputs..."))
        # If source was not found, throw an exception to indicate that the algorithm
        # encountered a fatal error. The exception text can be any string, but in this
        # case we use the pre-built invalidSourceError method to return a standard
        # helper text for when a source cannot be evaluated
        if basic_distance_cost is None or basic_distance_cost == 0:
            raise QgsProcessingException(
                self.invalidSourceError(parameters, self.BASIC_DISTANCE_COST))

        # Now, we check to see if the CRS, extent and resolution of every raster is equal, so that their align properly.
        listOfRastersToCheck = list()
        if initial_road_network_raster is not None:
            listOfRastersToCheck.append(initial_road_network_raster)
        if coarse_elevation_raster is not None:
            listOfRastersToCheck.append(coarse_elevation_raster)
        if fine_elevation_raster is not None:
            listOfRastersToCheck.append(fine_elevation_raster)
        if coarse_water_raster is not None:
            listOfRastersToCheck.append(coarse_water_raster)
        if fine_water_raster is not None:
            listOfRastersToCheck.append(fine_water_raster)
        if soil_raster is not None:
            listOfRastersToCheck.append(soil_raster)
        if special_raster is not None:
            listOfRastersToCheck.append(special_raster)

        if len(listOfRastersToCheck) == 0:
            raise QgsProcessingException(
                self.
                tr("At least one input raster is needed ! Please, input one raster."
                   ))

        # We check that every raster has the same CRS, extent and resolution.
        CostRasterCreatorHelper.CheckRastersCompatibility(listOfRastersToCheck)

        # We also check that the matrix of parameters entered for the coarse elevation costs and fine elevation costs
        # are correct : meaning that there are no holes between the thresholds, and that every lower threshold is lower
        # than the upper threshold.
        if coarse_elevation_costs is not None:
            CostRasterCreatorHelper.CheckThresholds(coarse_elevation_costs,
                                                    "Coarse elevation costs")
        if fine_elevation_costs is not None:
            CostRasterCreatorHelper.CheckThresholds(fine_elevation_costs,
                                                    "Fine elevation costs")

        # Then, we create the raster blocks
        if initial_road_network_raster is not None:
            initial_road_network_raster_block = CostRasterCreatorHelper.get_all_block(
                initial_road_network_raster)
        else:
            initial_road_network_raster_block = None

        if coarse_elevation_raster is not None:
            coarse_elevation_raster_block = CostRasterCreatorHelper.get_all_block(
                coarse_elevation_raster)
        else:
            coarse_elevation_raster_block = None

        if fine_elevation_raster is not None:
            fine_elevation_raster_block = CostRasterCreatorHelper.get_all_block(
                fine_elevation_raster)
        else:
            fine_elevation_raster_block = None

        if coarse_water_raster is not None:
            coarse_water_raster_block = CostRasterCreatorHelper.get_all_block(
                coarse_water_raster)
        else:
            coarse_water_raster_block = None

        if fine_water_raster is not None:
            fine_water_raster_block = CostRasterCreatorHelper.get_all_block(
                fine_water_raster)
        else:
            fine_water_raster_block = None

        if soil_raster is not None:
            soil_raster_block = CostRasterCreatorHelper.get_all_block(
                soil_raster)
        else:
            soil_raster_block = None

        if special_raster is not None:
            special_raster_block = CostRasterCreatorHelper.get_all_block(
                special_raster)
        else:
            special_raster_block = None

        feedback.pushInfo(self.tr("Preparing output..."))
        # We set the output to be ready : It is a QgsDataProvider
        outputFile = self.parameterAsOutputLayer(parameters, self.OUTPUT,
                                                 context)
        outputFormat = QgsRasterFileWriter.driverForExtension(
            os.path.splitext(outputFile)[1])

        crs = listOfRastersToCheck[0].crs()
        extent = listOfRastersToCheck[0].extent()
        rows = max([
            math.ceil(extent.height() /
                      listOfRastersToCheck[0].rasterUnitsPerPixelY()), 1.0
        ])
        cols = max([
            math.ceil(extent.width() /
                      listOfRastersToCheck[0].rasterUnitsPerPixelX()), 1.0
        ])
        # We will need this value for the fine water computation later
        pixelSide = (listOfRastersToCheck[0].rasterUnitsPerPixelY() +
                     listOfRastersToCheck[0].rasterUnitsPerPixelX()) / 2

        writer = QgsRasterFileWriter(outputFile)
        writer.setOutputProviderKey('gdal')
        writer.setOutputFormat(outputFormat)
        provider = writer.createOneBandRaster(Qgis.Float32, cols, rows, extent,
                                              crs)

        if provider is None:
            raise QgsProcessingException(
                self.tr("Could not create raster output: {}").format(
                    outputFile))
        if not provider.isValid():
            raise QgsProcessingException(
                self.tr("Could not create raster output {}").format(
                    outputFile))

        provider.setNoDataValue(1, -9999)

        # We create the data block for the output raster, and we fill it with the values we need
        dataBlock = QgsRasterBlock(Qgis.Float32, cols, rows)
        # i = float(1.0)

        feedback.pushInfo(self.tr("Calculating cost raster..."))
        progress = 0
        feedback.setProgress(0)
        errorMessages = list()
        for y in range(dataBlock.height()):
            for x in range(dataBlock.width()):
                if feedback.isCanceled():
                    raise QgsProcessingException(
                        self.tr("ERROR: Operation was cancelled."))

                    # If there is a road already on this pixel, then the cost is 0.
                if initial_road_network_raster_block is not None and \
                     (initial_road_network_raster_block.value(y,
                                                              x) != 0 and not initial_road_network_raster_block.isNoData(
                         y, x)):
                    finalValue = 0

                # If there is a water body on this pixel, we stop everything :
                # The cost will be the construction of a bridge
                elif coarse_water_raster_block is not None and coarse_water_raster_block.value(
                        y, x) != 0:
                    if coarse_water_cost is not None:
                        finalValue = coarse_water_cost
                    else:
                        raise QgsProcessingException(
                            self.
                            tr("A coarse water raster has been given, but no coarse "
                               +
                               "water cost. Please input a coarse water cost.")
                        )
                    # feedback.pushInfo("Seems like there was a road on this pixel. Final value is " + str(finalValue))

                # Else, if we are not on a road or on a body of water...
                else:
                    # We start with the base cost of crossing the pixel
                    finalValue = basic_distance_cost
                    # feedback.pushInfo("No road on this pixel, we put basic distance cost. Final value is " + str(finalValue))
                    # Then, if we have it, we add the soil cost
                    if soil_raster_block is not None:
                        finalValue += soil_raster_block.value(y, x)
                        # feedback.pushInfo("After soils, final value is " + str(finalValue))
                    if special_raster_block is not None:
                        finalValue += special_raster_block.value(y, x)
                    # Then the coarse elevation value
                    if coarse_elevation_raster_block is not None:
                        additionalValue, errorMessage = CostRasterCreatorHelper.CalculateCoarseElevationCost(
                            y, x, coarse_elevation_raster_block,
                            coarse_elevation_costs, pixelSide)
                        finalValue += additionalValue
                        if errorMessage is not None:
                            errorMessages.append(errorMessage)
                        # feedback.pushInfo("After coarse elevation, final value is " + str(finalValue))
                    # Then the fine water value
                    if fine_water_raster_block is not None:
                        finalValue += CostRasterCreatorHelper.CalculateFineWaterCost(
                            y, x, fine_water_raster_block, fine_water_cost,
                            pixelSide)
                        # feedback.pushInfo("After fine water, final value is " + str(finalValue))
                    # Then, we multiply everything with the fine elevation cost.
                    if fine_elevation_raster_block is not None:
                        finalValue, errorMessage = CostRasterCreatorHelper.CalculateFineElevationCost(
                            y, x, fine_elevation_raster_block,
                            fine_elevation_costs, finalValue)
                        if errorMessage is not None:
                            errorMessages.append(errorMessage)
                        # feedback.pushInfo("After fine elevation, final value is " + str(finalValue))
                dataBlock.setValue(y, x, float(finalValue))

                progress += 1
                feedback.setProgress(
                    100 * (progress /
                           (dataBlock.height() * dataBlock.width())))

        # We write the values in the provider of our files
        provider.writeBlock(dataBlock, 1)

        # We stop the edition of the output raster
        provider.setEditable(False)

        # We display the warning messages about the thresholds
        if len(errorMessages) > 0:
            above = 0
            below = 0
            for errorMessage in errorMessages:
                if errorMessage == "Above":
                    above += 1
                elif errorMessage == "Below":
                    below += 1
            feedback.pushInfo(
                self.
                tr("WARNING : There were " + str(below) +
                   " situations where the value of a pixel was under"
                   " the lowest threshold given as a parameter; and " +
                   str(above) + " when it was above the"
                   " highest. In those cases, the lowest or highest value of the parameter range was used."
                   " To avoid that, please make sure to use thresholds that cover all of the range of values in"
                   " your rasters."))

        # We make the output
        return {self.OUTPUT: outputFile}