def processAlgorithm(self, parameters, context, feedback): crs = self.parameterAsCrs(parameters, self.TARGET_CRS, context) extent = self.parameterAsExtent(parameters, self.EXTENT, context, crs) value = self.parameterAsDouble(parameters, self.NUMBER, context) pixelSize = self.parameterAsDouble(parameters, self.PIXEL_SIZE, context) outputFile = self.parameterAsOutputLayer(parameters, self.OUTPUT, context) outputFormat = QgsRasterFileWriter.driverForExtension(os.path.splitext(outputFile)[1]) rows = max([math.ceil(extent.height() / pixelSize) + 1, 1.0]) cols = max([math.ceil(extent.width() / pixelSize) + 1, 1.0]) writer = QgsRasterFileWriter(outputFile) writer.setOutputProviderKey('gdal') writer.setOutputFormat(outputFormat) provider = writer.createOneBandRaster(Qgis.Float32, cols, rows, extent, crs) provider.setNoDataValue(1, -9999) data = [value] * cols block = QgsRasterBlock(Qgis.Float32, cols, 1) block.setData(struct.pack('{}f'.format(len(data)), *data)) total = 100.0 / rows if rows else 0 for i in range(rows): if feedback.isCanceled(): break provider.writeBlock(block, 1, 0, i) feedback.setProgress(int(i * rows)) provider.setEditable(False) return {self.OUTPUT: outputFile}
def write_block(self, const_values=None, low_pass_filter=False): """ Construct raster block for each band, apply the values and write to file. If const_values are given (a list of const values for each band) they are used for each selected cell. In other case the memory layer with values calculated for each cell selected will be used. Alternatively, selected cells values can be filtered using low-pass 3x3 filter. """ if self.logger: vals = f"const values ({const_values})" if const_values else "expression values." self.logger.debug(f"Writing blocks with {vals}") if not self.provider.isEditable(): res = self.provider.setEditable(True) if not res: if self.uc: self.uc.show_warn('QGIS can\'t modify this type of raster') return None if self.logger: self.logger.debug("Calculating block origin coordinates...") b_orig_x, b_orig_y = self.index_to_point(self.block_row_min, self.block_col_min) cols = self.block_col_max - self.block_col_min + 1 rows = self.block_row_max - self.block_row_min + 1 b_end_x = b_orig_x + cols * self.pixel_size_x b_end_y = b_orig_y - rows * self.pixel_size_y block_bbox = QgsRectangle(b_orig_x, b_end_y, b_end_x, b_orig_y) if self.logger: self.logger.debug(f"Block bbox: {block_bbox.toString()}") self.logger.debug( f"Nr of cells in the block: rows={rows}, cols={cols}") old_blocks = [] new_blocks = [] cell_values = dict() if const_values is None and not low_pass_filter: for feat in self.cell_pts_layer.getFeatures(): cell_values[feat.id()] = feat.attribute(self.exp_field_idx) for band_nr in self.active_bands: block = self.provider.block(band_nr, block_bbox, cols, rows) new_blocks.append(block) block_data = block.data().data() old_block = QgsRasterBlock(self.data_types[band_nr - 1], cols, rows) old_block.setData(block_data) for abs_row, abs_col in self.selected_cells: row = abs_row - self.block_row_min col = abs_col - self.block_col_min if const_values: idx = band_nr - 1 if len(self.active_bands) > 1 else 0 new_val = const_values[idx] elif low_pass_filter: # the filter is applied for cells inside the block only if block.height() < 3 or block.width() < 3: # the selected block is too small for filtering -> keep the old value new_val = None else: new_val = low_pass_filtered( old_block, row, col, self.nodata_values[band_nr - 1]) else: # set the expression value feat_id = self.selected_cells_feats[(abs_row, abs_col)] if cell_values[feat_id] is not None: new_val = None if math.isnan(cell_values[feat_id]) or \ cell_values[feat_id] is None else cell_values[feat_id] else: new_val = None new_val = old_block.value(row, col) if new_val is None else new_val set_res = block.setValue(row, col, new_val) if self.logger: self.logger.debug( f"Setting block value for band {band_nr}, row {row}, col: {col}: {set_res}" ) old_blocks.append(old_block) band_res = self.provider.writeBlock(block, band_nr, self.block_col_min, self.block_row_min) if self.logger: self.logger.debug( f"Writing block for band {band_nr}: {band_res}") self.provider.setEditable(False) change = RasterChange(self.active_bands, self.block_row_min, self.block_col_min, old_blocks, new_blocks) self.raster_changed.emit(change) return True