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()
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
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}