def handleAlgorithmResults(alg, context, feedback=None, showResults=True): wrongLayers = [] if feedback is None: feedback = QgsProcessingFeedback() feedback.setProgressText(QCoreApplication.translate('Postprocessing', 'Loading resulting layers')) i = 0 for l, details in context.layersToLoadOnCompletion().items(): if feedback.isCanceled(): return False if len(context.layersToLoadOnCompletion()) > 2: # only show progress feedback if we're loading a bunch of layers feedback.setProgress(100 * i / float(len(context.layersToLoadOnCompletion()))) try: layer = QgsProcessingUtils.mapLayerFromString(l, context) if layer is not None: if not ProcessingConfig.getSetting(ProcessingConfig.USE_FILENAME_AS_LAYER_NAME): layer.setName(details.name) style = None if details.outputName: style = RenderingStyles.getStyle(alg.id(), details.outputName) if style is None: if layer.type() == QgsMapLayer.RasterLayer: style = ProcessingConfig.getSetting(ProcessingConfig.RASTER_STYLE) else: if layer.geometryType() == QgsWkbTypes.PointGeometry: style = ProcessingConfig.getSetting(ProcessingConfig.VECTOR_POINT_STYLE) elif layer.geometryType() == QgsWkbTypes.LineGeometry: style = ProcessingConfig.getSetting(ProcessingConfig.VECTOR_LINE_STYLE) else: style = ProcessingConfig.getSetting(ProcessingConfig.VECTOR_POLYGON_STYLE) if style: layer.loadNamedStyle(style) details.project.addMapLayer(context.temporaryLayerStore().takeMapLayer(layer)) if details.postProcessor(): details.postProcessor().postProcessLayer(layer, context, feedback) else: wrongLayers.append(str(l)) except Exception: QgsMessageLog.logMessage(QCoreApplication.translate('Postprocessing', "Error loading result layer:") + "\n" + traceback.format_exc(), 'Processing', Qgis.Critical) wrongLayers.append(str(l)) i += 1 feedback.setProgress(100) if wrongLayers: msg = QCoreApplication.translate('Postprocessing', "The following layers were not correctly generated.") msg += "<ul>" + "".join(["<li>%s</li>" % lay for lay in wrongLayers]) + "</ul>" msg += QCoreApplication.translate('Postprocessing', "You can check the 'Log Messages Panel' in QGIS main window to find more information about the execution of the algorithm.") feedback.reportError(msg) return len(wrongLayers) == 0
def handleAlgorithmResults(alg, context, feedback=None, showResults=True): wrongLayers = [] if feedback is None: feedback = QgsProcessingFeedback() feedback.setProgressText(QCoreApplication.translate('Postprocessing', 'Loading resulting layers')) i = 0 for l, details in context.layersToLoadOnCompletion().items(): if feedback.isCanceled(): return False if len(context.layersToLoadOnCompletion()) > 2: # only show progress feedback if we're loading a bunch of layers feedback.setProgress(100 * i / float(len(context.layersToLoadOnCompletion()))) try: layer = QgsProcessingUtils.mapLayerFromString(l, context, typeHint=details.layerTypeHint) if layer is not None: set_layer_name(layer, details) style = None if details.outputName: style = RenderingStyles.getStyle(alg.id(), details.outputName) if style is None: if layer.type() == QgsMapLayer.RasterLayer: style = ProcessingConfig.getSetting(ProcessingConfig.RASTER_STYLE) else: if layer.geometryType() == QgsWkbTypes.PointGeometry: style = ProcessingConfig.getSetting(ProcessingConfig.VECTOR_POINT_STYLE) elif layer.geometryType() == QgsWkbTypes.LineGeometry: style = ProcessingConfig.getSetting(ProcessingConfig.VECTOR_LINE_STYLE) else: style = ProcessingConfig.getSetting(ProcessingConfig.VECTOR_POLYGON_STYLE) if style: layer.loadNamedStyle(style) details.project.addMapLayer(context.temporaryLayerStore().takeMapLayer(layer)) if details.postProcessor(): details.postProcessor().postProcessLayer(layer, context, feedback) else: wrongLayers.append(str(l)) except Exception: QgsMessageLog.logMessage(QCoreApplication.translate('Postprocessing', "Error loading result layer:") + "\n" + traceback.format_exc(), 'Processing', Qgis.Critical) wrongLayers.append(str(l)) i += 1 feedback.setProgress(100) if wrongLayers: msg = QCoreApplication.translate('Postprocessing', "The following layers were not correctly generated.") msg += "<ul>" + "".join(["<li>%s</li>" % lay for lay in wrongLayers]) + "</ul>" msg += QCoreApplication.translate('Postprocessing', "You can check the 'Log Messages Panel' in QGIS main window to find more information about the execution of the algorithm.") feedback.reportError(msg) return len(wrongLayers) == 0
def handle_algorithm_results(alg: QgsProcessingAlgorithm, context: QgsProcessingContext, feedback: QgsProcessingFeedback, parameters={}, **kwargs) -> bool: """ Handle algorithms result layeri Insert result layers into destination project """ wrongLayers = [] for l, details in context.layersToLoadOnCompletion().items(): if feedback.isCanceled(): return False try: # Take as layer layer = QgsProcessingUtils.mapLayerFromString( l, context, typeHint=details.layerTypeHint) if layer is not None: # Set layer name to details name # This is because we enforce destination name beeing # the name from the parameter # see processing.io for how is handled layer destination naming layer.setName(details.name) LOGGER.debug("Layer name set to %s <details name: %s>", layer.name(), details.name) _set_output_layer_style(l, layer, alg, details, context, parameters) # Add layer to destination project LOGGER.debug( "Adding Map layer '%s' (outputName %s) to Qgs Project", l, details.outputName) details.project.addMapLayer( context.temporaryLayerStore().takeMapLayer(layer)) # Handle post processing if details.postProcessor(): details.postProcessor().postProcessLayer( layer, context, feedback) else: LOGGER.warning("No layer found for %s", l) except Exception: LOGGER.error("Processing: Error loading result layer:\n{}".format( traceback.format_exc())) wrongLayers.append(str(l)) if wrongLayers: msg = "The following layers were not correctly generated:" msg += "\n".join("%s" % lay for lay in wrongLayers) msg += "You can check the log messages to find more information about the execution of the algorithm" feedback.reportError(msg) return len(wrongLayers) == 0
def execute_in_place_run(alg, parameters, context=None, feedback=None, raise_exceptions=False): """Executes an algorithm modifying features in-place in the input layer. :param alg: algorithm to run :type alg: QgsProcessingAlgorithm :param parameters: parameters of the algorithm :type parameters: dict :param context: context, defaults to None :type context: QgsProcessingContext, optional :param feedback: feedback, defaults to None :type feedback: QgsProcessingFeedback, optional :param raise_exceptions: useful for testing, if True exceptions are raised, normally exceptions will be forwarded to the feedback :type raise_exceptions: boo, default to False :raises QgsProcessingException: raised when there is no active layer, or it cannot be made editable :return: a tuple with true if success and results :rtype: tuple """ if feedback is None: feedback = QgsProcessingFeedback() if context is None: context = dataobjects.createContext(feedback) # Only feature based algs have sourceFlags try: if alg.sourceFlags( ) & QgsProcessingFeatureSource.FlagSkipGeometryValidityChecks: context.setInvalidGeometryCheck(QgsFeatureRequest.GeometryNoCheck) except AttributeError: pass active_layer = parameters['INPUT'] # Run some checks and prepare the layer for in-place execution by: # - getting the active layer and checking that it is a vector # - making the layer editable if it was not already # - selecting all features if none was selected # - checking in-place support for the active layer/alg/parameters # If one of the check fails and raise_exceptions is True an exception # is raised, else the execution is aborted and the error reported in # the feedback try: if active_layer is None: raise QgsProcessingException(tr("There is not active layer.")) if not isinstance(active_layer, QgsVectorLayer): raise QgsProcessingException( tr("Active layer is not a vector layer.")) if not active_layer.isEditable(): if not active_layer.startEditing(): raise QgsProcessingException( tr("Active layer is not editable (and editing could not be turned on)." )) if not alg.supportInPlaceEdit(active_layer): raise QgsProcessingException( tr("Selected algorithm and parameter configuration are not compatible with in-place modifications." )) except QgsProcessingException as e: if raise_exceptions: raise e QgsMessageLog.logMessage(str(sys.exc_info()[0]), 'Processing', Qgis.Critical) if feedback is not None: feedback.reportError(getattr(e, 'msg', str(e)), fatalError=True) return False, {} if not active_layer.selectedFeatureIds(): active_layer.selectAll() # Make sure we are working on selected features only parameters['INPUT'] = QgsProcessingFeatureSourceDefinition( active_layer.id(), True) parameters['OUTPUT'] = 'memory:' req = QgsFeatureRequest(QgsExpression(r"$id < 0")) req.setFlags(QgsFeatureRequest.NoGeometry) req.setSubsetOfAttributes([]) # Start the execution # If anything goes wrong and raise_exceptions is True an exception # is raised, else the execution is aborted and the error reported in # the feedback try: new_feature_ids = [] active_layer.beginEditCommand(alg.displayName()) # Checks whether the algorithm has a processFeature method if hasattr(alg, 'processFeature'): # in-place feature editing # Make a clone or it will crash the second time the dialog # is opened and run alg = alg.create() if not alg.prepare(parameters, context, feedback): raise QgsProcessingException( tr("Could not prepare selected algorithm.")) # Check again for compatibility after prepare if not alg.supportInPlaceEdit(active_layer): raise QgsProcessingException( tr("Selected algorithm and parameter configuration are not compatible with in-place modifications." )) field_idxs = range(len(active_layer.fields())) iterator_req = QgsFeatureRequest(active_layer.selectedFeatureIds()) iterator_req.setInvalidGeometryCheck( context.invalidGeometryCheck()) feature_iterator = active_layer.getFeatures(iterator_req) step = 100 / len(active_layer.selectedFeatureIds() ) if active_layer.selectedFeatureIds() else 1 for current, f in enumerate(feature_iterator): feedback.setProgress(current * step) if feedback.isCanceled(): break # need a deep copy, because python processFeature implementations may return # a shallow copy from processFeature input_feature = QgsFeature(f) new_features = alg.processFeature(input_feature, context, feedback) new_features = QgsVectorLayerUtils.makeFeaturesCompatible( new_features, active_layer) if len(new_features) == 0: active_layer.deleteFeature(f.id()) elif len(new_features) == 1: new_f = new_features[0] if not f.geometry().equals(new_f.geometry()): active_layer.changeGeometry(f.id(), new_f.geometry()) if f.attributes() != new_f.attributes(): active_layer.changeAttributeValues( f.id(), dict(zip(field_idxs, new_f.attributes())), dict(zip(field_idxs, f.attributes()))) new_feature_ids.append(f.id()) else: active_layer.deleteFeature(f.id()) # Get the new ids old_ids = set( [f.id() for f in active_layer.getFeatures(req)]) if not active_layer.addFeatures(new_features): raise QgsProcessingException( tr("Error adding processed features back into the layer." )) new_ids = set( [f.id() for f in active_layer.getFeatures(req)]) new_feature_ids += list(new_ids - old_ids) results, ok = {}, True else: # Traditional 'run' with delete and add features cycle # There is no way to know if some features have been skipped # due to invalid geometries if context.invalidGeometryCheck( ) == QgsFeatureRequest.GeometrySkipInvalid: selected_ids = active_layer.selectedFeatureIds() else: selected_ids = [] results, ok = alg.run(parameters, context, feedback) if ok: result_layer = QgsProcessingUtils.mapLayerFromString( results['OUTPUT'], context) # TODO: check if features have changed before delete/add cycle new_features = [] # Check if there are any skipped features if context.invalidGeometryCheck( ) == QgsFeatureRequest.GeometrySkipInvalid: missing_ids = list( set(selected_ids) - set(result_layer.allFeatureIds())) if missing_ids: for f in active_layer.getFeatures( QgsFeatureRequest(missing_ids)): if not f.geometry().isGeosValid(): new_features.append(f) active_layer.deleteFeatures(active_layer.selectedFeatureIds()) for f in result_layer.getFeatures(): new_features.extend( QgsVectorLayerUtils.makeFeaturesCompatible( [f], active_layer)) # Get the new ids old_ids = set([f.id() for f in active_layer.getFeatures(req)]) if not active_layer.addFeatures(new_features): raise QgsProcessingException( tr("Error adding processed features back into the layer." )) new_ids = set([f.id() for f in active_layer.getFeatures(req)]) new_feature_ids += list(new_ids - old_ids) active_layer.endEditCommand() if ok and new_feature_ids: active_layer.selectByIds(new_feature_ids) elif not ok: active_layer.rollBack() return ok, results except QgsProcessingException as e: active_layer.endEditCommand() active_layer.rollBack() if raise_exceptions: raise e QgsMessageLog.logMessage(str(sys.exc_info()[0]), 'Processing', Qgis.Critical) if feedback is not None: feedback.reportError(getattr(e, 'msg', str(e)), fatalError=True) return False, {}
def handleAlgorithmResults(alg, context, feedback=None, showResults=True): wrongLayers = [] if feedback is None: feedback = QgsProcessingFeedback() feedback.setProgressText( QCoreApplication.translate('Postprocessing', 'Loading resulting layers')) i = 0 for l, details in context.layersToLoadOnCompletion().items(): if feedback.isCanceled(): return False if len(context.layersToLoadOnCompletion()) > 2: # only show progress feedback if we're loading a bunch of layers feedback.setProgress( 100 * i / float(len(context.layersToLoadOnCompletion()))) try: layer = QgsProcessingUtils.mapLayerFromString(l, context) if layer is not None: layer.setName(details.name) details.project.addMapLayer( context.temporaryLayerStore().takeMapLayer(layer)) else: name = details.name if ProcessingConfig.getSetting( ProcessingConfig.USE_FILENAME_AS_LAYER_NAME): name = os.path.basename(l) dataobjects.load(l, name, alg.crs, RenderingStyles.getStyle(alg.id(), l)) except Exception: QgsMessageLog.logMessage( "Error loading result layer:\n" + traceback.format_exc(), 'Processing', QgsMessageLog.CRITICAL) #wrongLayers.append(out.description()) wrongLayers.append(l) # for out in alg.outputs: # feedback.setProgress(100 * i / float(len(alg.outputs))) # if out.flags() & QgsProcessingParameterDefinition.FlagHidden or not out.open: # continue # if isinstance(out, (OutputRaster, OutputVector, OutputTable)): # try: # layer = QgsProcessingUtils.mapLayerFromString(out.value, context) # if layer: # layer.setName(out.description) # QgsProject.instance().addMapLayer(context.temporaryLayerStore().takeMapLayer(layer)) # else: # if ProcessingConfig.getSetting( # ProcessingConfig.USE_FILENAME_AS_LAYER_NAME): # name = os.path.basename(out.value) # else: # name = out.description # # isRaster = True if isinstance(out, OutputRaster) else False # dataobjects.load(out.value, name, alg.crs, # RenderingStyles.getStyle(alg.id(), out.name), # isRaster) # except Exception: # QgsMessageLog.logMessage("Error loading result layer:\n" + traceback.format_exc(), 'Processing', QgsMessageLog.CRITICAL) # wrongLayers.append(out.description) # elif isinstance(out, OutputHTML): # resultsList.addResult(alg.icon(), out.description, out.value) i += 1 QApplication.restoreOverrideCursor() if wrongLayers: msg = "The following layers were not correctly generated.<ul>" msg += "".join(["<li>%s</li>" % lay for lay in wrongLayers]) + "</ul>" msg += "You can check the log messages to find more information about the execution of the algorithm" feedback.reportError(msg) return len(wrongLayers) == 0
def handleAlgorithmResults(alg, context, feedback=None, showResults=True, parameters={}): wrongLayers = [] if feedback is None: feedback = QgsProcessingFeedback() feedback.setProgressText(QCoreApplication.translate('Postprocessing', 'Loading resulting layers')) i = 0 for l, details in context.layersToLoadOnCompletion().items(): if feedback.isCanceled(): return False if len(context.layersToLoadOnCompletion()) > 2: # only show progress feedback if we're loading a bunch of layers feedback.setProgress(100 * i / float(len(context.layersToLoadOnCompletion()))) try: layer = QgsProcessingUtils.mapLayerFromString(l, context, typeHint=details.layerTypeHint) if layer is not None: set_layer_name(layer, details) '''If running a model, the execution will arrive here when an algorithm that is part of that model is executed. We check if its output is a final otuput of the model, and adapt the output name accordingly''' outputName = details.outputName expcontext = QgsExpressionContext() scope = QgsExpressionContextScope() expcontext.appendScope(scope) for out in alg.outputDefinitions(): if out.name() not in parameters: continue outValue = parameters[out.name()] if hasattr(outValue, "sink"): outValue = outValue.sink.valueAsString(expcontext)[0] else: outValue = str(outValue) if outValue == l: outputName = out.name() break style = None if outputName: style = RenderingStyles.getStyle(alg.id(), outputName) if style is None: if layer.type() == QgsMapLayer.RasterLayer: style = ProcessingConfig.getSetting(ProcessingConfig.RASTER_STYLE) else: if layer.geometryType() == QgsWkbTypes.PointGeometry: style = ProcessingConfig.getSetting(ProcessingConfig.VECTOR_POINT_STYLE) elif layer.geometryType() == QgsWkbTypes.LineGeometry: style = ProcessingConfig.getSetting(ProcessingConfig.VECTOR_LINE_STYLE) else: style = ProcessingConfig.getSetting(ProcessingConfig.VECTOR_POLYGON_STYLE) if style: layer.loadNamedStyle(style) details.project.addMapLayer(context.temporaryLayerStore().takeMapLayer(layer)) if details.postProcessor(): details.postProcessor().postProcessLayer(layer, context, feedback) else: wrongLayers.append(str(l)) except Exception: QgsMessageLog.logMessage(QCoreApplication.translate('Postprocessing', "Error loading result layer:") + "\n" + traceback.format_exc(), 'Processing', Qgis.Critical) wrongLayers.append(str(l)) i += 1 feedback.setProgress(100) if wrongLayers: msg = QCoreApplication.translate('Postprocessing', "The following layers were not correctly generated.") msg += "<ul>" + "".join(["<li>%s</li>" % lay for lay in wrongLayers]) + "</ul>" msg += QCoreApplication.translate('Postprocessing', "You can check the 'Log Messages Panel' in QGIS main window to find more information about the execution of the algorithm.") feedback.reportError(msg) return len(wrongLayers) == 0
class CartogramWorkOrchestratorMixIn: """Manage the tasks of the plugin’s workers.""" def __init__(self): """Manage the tasks of the plugin’s workers.""" super(CartogramWorkOrchestratorMixIn, self).__init__() self.add_processing_provider() def add_processing_provider(self): self.provider = CartogramProcessingProvider() QgsApplication.processingRegistry().addProvider(self.provider) def cancel_task(self): self.disable_cancel_button() self.task.cancel() def is_task_running(self): try: return self.task.isActive() except (AttributeError, RuntimeError): # (no self.task) return False def remove_processing_provider(self): QgsApplication.processingRegistry().removeProvider(self.provider) def sample_layer(self): source_layer = QgsVectorLayer( os.path.join(self.plugin_dir, "data", "Austria_PopulationByNUTS2.gml"), "" ) # (empty) memory layer sample_layer = QgsVectorLayer( QgsWkbTypes.geometryDisplayString(source_layer.geometryType()) + "?crs=" + source_layer.crs().authid() + "&index=yes", "Austria_Population_NUTS2_20170101", "memory" ) sample_layer_data_provider = sample_layer.dataProvider() sample_layer_data_provider.addAttributes(source_layer.fields().toList()) sample_layer.updateFields() sample_layer_data_provider.addFeatures(list(source_layer.getFeatures())) sample_layer.loadNamedStyle( os.path.join(self.plugin_dir, "data", "Austria_PopulationByNUTS2.qml") ) sample_layer.setTitle("Austria: Population by NUTS2 regions, 1 Jan 2017") sample_layer.setShortName("Austria_Population_NUTS2_20170101") sample_layer.setAbstract( "Austria’s population by NUTS2 region, as of 1 Jan 2017 \n" + "\n" + "Data sources: \n" + " http://ec.europa.eu/eurostat/web/gisco/geodata/" + "reference-data/administrative-units-statistical-units/" + "nuts#nuts13 \n" + " http://www.statistik.at/web_de/statistiken/" + "menschen_und_gesellschaft/bevoelkerung/" + "bevoelkerungsstand_und_veraenderung/" + "bevoelkerung_zu_jahres-_quartalsanfang/index.html" ) return sample_layer def start_task(self, input_layer, field, max_iterations, max_average_error): self.context = QgsProcessingContext() self.feedback = QgsProcessingFeedback() self.task = QgsProcessingAlgRunnerTask( QgsApplication.processingRegistry().algorithmById("cartogram3:compute_cartogram"), { "INPUT": input_layer, "FIELD": field, "MAX_ITERATIONS": max_iterations, "MAX_AVERAGE_ERROR": max_average_error, "OUTPUT": "memory:" }, self.context, self.feedback ) self.task.executed.connect(self.task_finished) self.feedback.progressChanged.connect(self.update_progress) QgsApplication.taskManager().addTask(self.task) def task_finished(self, successful, results={}): if successful: output_layer = self.context.getMapLayer(results["OUTPUT"]) if output_layer and output_layer.isValid(): layer = self.context.takeResultLayer(output_layer.id()) self.add_result_layer_to_map_canvas(layer, results["FIELD"]) self.feedback.pushInfo( ( "Finished computing cartogram for layer {:s} on field {:s} " + "after {:d} iterations with {:.2n}% residual error." ).format( self.input_layer.name(), results["FIELD"], results["ITERATIONS"], results["RESIDUAL_AVERAGE_ERROR"] ) ) else: if self.feedback.isCanceled(): self.feedback.pushWarning("User canceled cartogram computation") self.feedback.reportError("Failed to compute cartogram") self.clean_up_ui() # remove progress bar def update_progress(self, progress): self.update_progress_bar(progress)
def mergeDataSources2Vrt(self, dataSources, outFile, union=False, relative=False, schema=False, feedback=None): '''Function to do the work of merging datasources in a single vrt format @param data_sources: Array of path strings @param outfile: the output vrt file to generate @param relative: Write relative flag. DOES NOT relativise paths. They have to be already relative @param schema: Schema flag @return: vrt in string format ''' if feedback is None: feedback = QgsProcessingFeedback() vrt = '<OGRVRTDataSource>' if union: vrt += '<OGRVRTUnionLayer name="UnionedLayer">' total = 100.0 / len(dataSources) if dataSources else 1 for current, layer in enumerate(dataSources): if feedback.isCanceled(): break feedback.setProgress(int(current * total)) inFile = layer.source() srcDS = ogr.Open(inFile, 0) if srcDS is None: raise QgsProcessingException( self.tr('Invalid datasource: {}'.format(inFile))) if schema: inFile = '@dummy@' for layer in srcDS: layerDef = layer.GetLayerDefn() layerName = layerDef.GetName() vrt += '<OGRVRTLayer name="{}">'.format(self.XmlEsc(layerName)) vrt += '<SrcDataSource relativeToVRT="{}" shared="{}">{}</SrcDataSource>'.format(1 if relative else 0, not schema, self.XmlEsc(inFile)) if schema: vrt += '<SrcLayer>@dummy@</SrcLayer>' else: vrt += '<SrcLayer>{}</SrcLayer>'.format(self.XmlEsc(layerName)) vrt += '<GeometryType>{}</GeometryType>'.format(self.GeomType2Name(layerDef.GetGeomType())) crs = layer.GetSpatialRef() if crs is not None: vrt += '<LayerSRS>{}</LayerSRS>'.format(self.XmlEsc(crs.ExportToWkt())) # Process all the fields. for fieldIdx in range(layerDef.GetFieldCount()): fieldDef = layerDef.GetFieldDefn(fieldIdx) vrt += '<Field name="{}" type="{}"'.format(self.XmlEsc(fieldDef.GetName()), self.fieldType2Name(fieldDef.GetType())) if not schema: vrt += ' src="{}"'.format(self.XmlEsc(fieldDef.GetName())) if fieldDef.GetWidth() > 0: vrt += ' width="{}"'.format(fieldDef.GetWidth()) if fieldDef.GetPrecision() > 0: vrt += ' precision="{}"'.format(fieldDef.GetPrecision()) vrt += '/>' vrt += '</OGRVRTLayer>' srcDS.Destroy() if union: vrt += '</OGRVRTUnionLayer>' vrt += '</OGRVRTDataSource>' #TODO: pretty-print XML if outFile is not None: with codecs.open(outFile, 'w') as f: f.write(vrt) return vrt
def processAlgorithm(self, # type: ignore parameters: Dict[str, Any], context: QgsProcessingContext, feedback: QgsProcessingFeedback) -> Dict[str, Any]: resident_count = self.parameterAsInt(parameters, self.RESIDENT_COUNT, context) employee_count = self.parameterAsInt(parameters, self.EMPLOYEE_COUNT, context) ssa_value = self.parameterAsDouble(parameters, self.SSA, context) resident_employee_count = resident_count + employee_count if resident_employee_count <= 0: raise QgsProcessingException('Sum of resident and employee count can not equal 0 or less') if ssa_value < 0 or ssa_value > 1: raise QgsProcessingException('SSA value needs to be between 0 and 1 or less') outputs = {} # USL Clip Raster alg_params = { 'NO_DATA_VALUE': parameters[self.NO_DATA_VALUE], 'RASTER': parameters[self.RASTER], 'VECTOR': parameters[self.VECTOR], 'CLIPPED_RASTER': QgsProcessing.TEMPORARY_OUTPUT } outputs['UslClipRaster'] = processing.run('usl:usl_clip_raster', alg_params, context=context, feedback=feedback, is_child_algorithm=True) if feedback.isCanceled(): return {} # USL SI Calculator alg_params = { 'BUILD_UP_VALUE': parameters[self.BUILD_UP_VALUE], 'CLIPPED_RASTER': outputs['UslClipRaster']['CLIPPED_RASTER'], 'NO_DATA_VALUE': parameters[self.NO_DATA_VALUE], 'RADIUS': constants.RADIUS_VALUE, 'RASTER': parameters[self.RASTER], 'SI_RASTER': parameters[self.OUTPUT_RASTER] } outputs['UslSiCalculator'] = processing.run('usl:usl_si_calculator', alg_params, context=context, feedback=feedback, is_child_algorithm=True) if feedback.isCanceled(): return {} # USL DIS Calculator alg_params = { 'SI_RASTER': outputs['UslSiCalculator']['SI_RASTER'] } outputs['UslDisCalculator'] = processing.run('usl:usl_dis_calculator', alg_params, context=context, feedback=feedback, is_child_algorithm=True) if feedback.isCanceled(): return {} # USL LUP Calculator alg_params = { 'BUILD_UP_VALUE': parameters[self.BUILD_UP_VALUE], 'CLIPPED_RASTER': outputs['UslClipRaster']['CLIPPED_RASTER'], 'EMPLOYEE_COUNT': employee_count, 'RESIDENT_COUNT': resident_count } outputs['UslLupCalculator'] = processing.run('usl:usl_lup_calculator', alg_params, context=context, feedback=feedback, is_child_algorithm=True) if feedback.isCanceled(): return {} # USL WUP Calculator alg_params = { 'DIS': outputs['UslDisCalculator']['DIS'], 'LUP': outputs['UslLupCalculator']['LUP'], 'SSA': ssa_value } outputs['UslWupCalculator'] = processing.run('usl:usl_wup_calculator', alg_params, context=context, feedback=feedback, is_child_algorithm=True) feedback.pushInfo('WUP,DIS,LUP') feedback.pushInfo(outputs['UslWupCalculator']['WUP'], outputs['UslDisCalculator']['DIS'], outputs['UslLupCalculator']['LUP']) return {self.OUTPUT: outputs['UslWupCalculator']['WUP']}
def processAlgorithm(self, parameters, context, feedback: QgsProcessingFeedback): # Input / Output source = self.parameterAsSource(parameters, self.INPUT, context) (sink, dest_id) = self.create_output_sink(parameters, context, source.sourceCrs()) # Filter parameters observation_type_indices = self.parameterAsEnums( parameters, self.OBSERVATION_TYPE, context) observation_types = list( map(lambda i: self.OBSERVATION_TYPES[i][0], observation_type_indices)) from_date = None from_date_string = self.parameterAsString(parameters, self.FROM_DATE, context) if from_date_string: from_date = datetime.fromisoformat(from_date_string) to_date = None to_date_string = self.parameterAsString(parameters, self.TO_DATE, context) if to_date_string: to_date = datetime.fromisoformat(to_date_string) fire_connection_string = self.settings.value("fire_connection_string") fireDb = FireDb(fire_connection_string, debug=True) features = list(source.getFeatures()) total_num_features = len(features) total_num_features_processed = 0 # for current, feature in enumerate(features): for feature in features: if feedback.isCanceled(): return {} wkt = feature.geometry().asWkt().upper() geometry = Geometry(wkt) observations = fireDb.hent_observationer_naer_geometri( geometri=geometry, afstand=0, tidfra=from_date, tidtil=to_date) pid_list = self.get_pids_from_observations(observations) geometriobjekter = self.get_geometriobjekter_from_pids( fireDb, pid_list) idents = self.get_idents_from_pids(fireDb, pid_list) feedback.setProgressText( "Fandt {antal} observationer".format(antal=len(observations))) feedback.setProgressText("Fandt {antal} geometriobjekter".format( antal=len(geometriobjekter))) feedback.setProgressText( "Fandt {antal} idents".format(antal=len(idents))) for current, observation in enumerate(observations): observation_type_id = observation.observationstypeid if observation_type_id in observation_types: feature = self.create_feature_from_observation( observation, geometriobjekter, idents, feedback) if feature: sink.addFeature(feature, QgsFeatureSink.FastInsert) total_num_features_processed = total_num_features_processed + 1 feedback.setProgress(total_num_features_processed / total_num_features) if feedback.isCanceled(): return {} apply_theme = self.parameterAsBool(parameters, self.APPLY_THEME, context) if apply_theme: style_file = os.path.join(os.path.dirname(__file__), "..", "styles", "observation.qml") alg_params = {"INPUT": dest_id, "STYLE": style_file} processing.run( "qgis:setstyleforvectorlayer", alg_params, context=context, feedback=feedback, is_child_algorithm=True, ) return {self.OUTPUT: dest_id}
def execute_in_place_run(alg, active_layer, parameters, context=None, feedback=None, raise_exceptions=False): """Executes an algorithm modifying features in-place in the input layer. The input layer must be editable or an exception is raised. :param alg: algorithm to run :type alg: QgsProcessingAlgorithm :param active_layer: the editable layer :type active_layer: QgsVectoLayer :param parameters: parameters of the algorithm :type parameters: dict :param context: context, defaults to None :param context: QgsProcessingContext, optional :param feedback: feedback, defaults to None :param feedback: QgsProcessingFeedback, optional :raises QgsProcessingException: raised when the layer is not editable or the layer cannot be found in the current project :return: a tuple with true if success and results :rtype: tuple """ if feedback is None: feedback = QgsProcessingFeedback() if context is None: context = dataobjects.createContext(feedback) if active_layer is None or not active_layer.isEditable(): raise QgsProcessingException(tr("Layer is not editable or layer is None.")) if not alg.supportInPlaceEdit(active_layer): raise QgsProcessingException(tr("Selected algorithm and parameter configuration are not compatible with in-place modifications.")) parameters['OUTPUT'] = 'memory:' try: new_feature_ids = [] active_layer.beginEditCommand(alg.displayName()) req = QgsFeatureRequest(QgsExpression(r"$id < 0")) req.setFlags(QgsFeatureRequest.NoGeometry) req.setSubsetOfAttributes([]) # Checks whether the algorithm has a processFeature method if hasattr(alg, 'processFeature'): # in-place feature editing # Make a clone or it will crash the second time the dialog # is opened and run alg = alg.create() if not alg.prepare(parameters, context, feedback): raise QgsProcessingException(tr("Could not prepare selected algorithm.")) # Check again for compatibility after prepare if not alg.supportInPlaceEdit(active_layer): raise QgsProcessingException(tr("Selected algorithm and parameter configuration are not compatible with in-place modifications.")) field_idxs = range(len(active_layer.fields())) feature_iterator = active_layer.getFeatures(QgsFeatureRequest(active_layer.selectedFeatureIds())) if parameters['INPUT'].selectedFeaturesOnly else active_layer.getFeatures() step = 100 / len(active_layer.selectedFeatureIds()) if active_layer.selectedFeatureIds() else 1 for current, f in enumerate(feature_iterator): feedback.setProgress(current * step) if feedback.isCanceled(): break # need a deep copy, because python processFeature implementations may return # a shallow copy from processFeature input_feature = QgsFeature(f) new_features = alg.processFeature(input_feature, context, feedback) new_features = QgsVectorLayerUtils.makeFeaturesCompatible(new_features, active_layer) if len(new_features) == 0: active_layer.deleteFeature(f.id()) elif len(new_features) == 1: new_f = new_features[0] if not f.geometry().equals(new_f.geometry()): active_layer.changeGeometry(f.id(), new_f.geometry()) if f.attributes() != new_f.attributes(): active_layer.changeAttributeValues(f.id(), dict(zip(field_idxs, new_f.attributes())), dict(zip(field_idxs, f.attributes()))) new_feature_ids.append(f.id()) else: active_layer.deleteFeature(f.id()) # Get the new ids old_ids = set([f.id() for f in active_layer.getFeatures(req)]) if not active_layer.addFeatures(new_features): raise QgsProcessingException(tr("Error adding processed features back into the layer.")) new_ids = set([f.id() for f in active_layer.getFeatures(req)]) new_feature_ids += list(new_ids - old_ids) results, ok = {}, True else: # Traditional 'run' with delete and add features cycle results, ok = alg.run(parameters, context, feedback) if ok: result_layer = QgsProcessingUtils.mapLayerFromString(results['OUTPUT'], context) # TODO: check if features have changed before delete/add cycle active_layer.deleteFeatures(active_layer.selectedFeatureIds()) new_features = [] for f in result_layer.getFeatures(): new_features.extend(QgsVectorLayerUtils. makeFeaturesCompatible([f], active_layer)) # Get the new ids old_ids = set([f.id() for f in active_layer.getFeatures(req)]) if not active_layer.addFeatures(new_features): raise QgsProcessingException(tr("Error adding processed features back into the layer.")) new_ids = set([f.id() for f in active_layer.getFeatures(req)]) new_feature_ids += list(new_ids - old_ids) active_layer.endEditCommand() if ok and new_feature_ids: active_layer.selectByIds(new_feature_ids) elif not ok: active_layer.rollBack() return ok, results except QgsProcessingException as e: active_layer.endEditCommand() active_layer.rollBack() if raise_exceptions: raise e QgsMessageLog.logMessage(str(sys.exc_info()[0]), 'Processing', Qgis.Critical) if feedback is not None: feedback.reportError(getattr(e, 'msg', str(e))) return False, {}
def processAlgorithm(self, parameters: Dict[str, Any], context: QgsProcessingContext, feedback: QgsProcessingFeedback) -> Union[dict, Dict[str, Any]]: result = dict() canalsfield: str = self.parameterAsFields(parameters, self.CANALSFIELD, context)[0] pointsfield: str = self.parameterAsFields(parameters, self.POINTSFIELD, context)[0] typefield: str = self.parameterAsFields(parameters, self.TYPEFIELD, context)[0] typevalue: str = self.parameterAsString(parameters, self.TYPEVALUE, context) tolerancepoints: float = self.parameterAsDouble(parameters, self.TOLERANCEPOINTS, context) tolerancecanals: float = self.parameterAsDouble(parameters, self.TOLERANCECANALS, context) model_feedback = QgsProcessingMultiStepFeedback(3, feedback) if feedback.isCanceled(): return result snappedcanals_out_name = 'native:splitwithlines_1:{}'.format(self.SNAPPEDCANALS) intersect_out_name = 'native:fieldcalculator_1:INTERSECTIONS' points_with_uuid_name = 'qgis:advancedpythonfieldcalculator_1:{}'.format(self.POINTSWITHUUID) proc_result = processing.run(self.snap_lines.model(), { 'CANALS': parameters[self.CANALS], 'DELIVERYPOINTS': parameters[self.DELIVERYPOINTS], 'TOLERANCECANALS': tolerancecanals, 'TYPEFIELD': typefield, 'TYPEVALUE': typevalue, 'VERBOSE_LOG': True, snappedcanals_out_name: 'TEMPORARY_OUTPUT', intersect_out_name: 'TEMPORARY_OUTPUT', points_with_uuid_name: 'TEMPORARY_OUTPUT', }, context=context, feedback=model_feedback, is_child_algorithm=True) # TODO SNAPPEDCANALS добавить в result в конце, когда они будут порезаны по пересечениям print("Main processing complete") model_feedback.setCurrentStep(1) if feedback.isCanceled(): return result snapped_canals: QgsVectorLayer = context.takeResultLayer( proc_result[snappedcanals_out_name]) intersections: QgsVectorLayer = context.takeResultLayer( proc_result[intersect_out_name]) points_with_uuid: QgsVectorLayer = context.takeResultLayer( proc_result[points_with_uuid_name]) (snapped_canals_sink, snapped_canals_id) = self.parameterAsSink(parameters, self.SNAPPEDCANALS, context, snapped_canals.fields(), snapped_canals.wkbType(), snapped_canals.sourceCrs()) (points_with_uuid_sink, points_with_uuid_id) = self.parameterAsSink(parameters, self.POINTSWITHUUID, context, points_with_uuid.fields(), points_with_uuid.wkbType(), points_with_uuid.sourceCrs()) snapped_canals_sink.addFeatures(snapped_canals.getFeatures()) points_with_uuid_sink.addFeatures(points_with_uuid.getFeatures()) result.update({ self.SNAPPEDCANALS: snapped_canals_id, self.POINTSWITHUUID: points_with_uuid_id, }) snapped_points_layers = [] for line_name in points_with_uuid.uniqueValues(points_with_uuid.fields().indexFromName(pointsfield)): line_expression = QgsExpression().createFieldEqualityExpression(canalsfield, line_name) line_request = QgsFeatureRequest() line_request.setFilterExpression(line_expression) point_expression = QgsExpression().createFieldEqualityExpression(pointsfield, line_name) point_request = QgsFeatureRequest() point_request.setFilterExpression(point_expression) subset_snapped_canals = snapped_canals.materialize(line_request, model_feedback) subset_points_with_uuid = points_with_uuid.materialize(point_request, model_feedback) snapped_points_layers.append( processing.run("native:snapgeometries", { 'INPUT': subset_points_with_uuid, 'REFERENCE_LAYER': subset_snapped_canals, 'TOLERANCE': tolerancepoints, 'BEHAVIOR': 1, 'OUTPUT': 'TEMPORARY_OUTPUT' }, context=context, # feedback=model_feedback, is_child_algorithm=True)['OUTPUT'] ) print("Cycle complete") model_feedback.setCurrentStep(2) if feedback.isCanceled(): return result snapped_points_layers.append(intersections) snapped_points_id = processing.run("native:mergevectorlayers", { 'LAYERS': snapped_points_layers, 'CRS': points_with_uuid.crs(), 'OUTPUT': 'TEMPORARY_OUTPUT' }, context=context, feedback=model_feedback, is_child_algorithm=True)['OUTPUT'] snapped_points: QgsVectorLayer = context.takeResultLayer(snapped_points_id) (snapped_points_sink, snapped_points_id) = self.parameterAsSink(parameters, self.SNAPPEDPOINTS, context, snapped_points.fields(), snapped_points.wkbType(), snapped_points.sourceCrs()) snapped_points_sink.addFeatures(snapped_points.getFeatures()) print("Vectors merged") model_feedback.setCurrentStep(3) if feedback.isCanceled(): return result result.update({ self.SNAPPEDPOINTS: snapped_points_id, }) print(result) return result
def generate_od_routes( network_layer: QgsVectorLayer, origin_layer: QgsVectorLayer, poi_layer: QgsVectorLayer, size_field: str, class_field: str, work_layer: QgsVectorLayer = None, work_size_field: str = None, school_layer: QgsVectorLayer = None, school_size_field: str = None, origin_weight_field: str = None, socio_data=None, health_data=None, diversity_data=None, join_on: str = None, max_distance: int = 25000, return_layer: bool = True, return_raw: bool = False, feedback: QgsProcessingFeedback = None, ) -> QgsVectorLayer: """ Shortest path algorithm based on Dijkstra's algorithm :param network_layer: road network :param points_layer: combined from to points :param relations_data: tabular from to id data :param origin_field: name of from field :param destination_field: name of to field :param max_distance: maximum distance/cost :param crs: output layer crs """ if not network_layer.wkbType() & QgsWkbTypes.LineString: raise Exception('Network layer must be of type LineString') crs = network_layer.crs() ## prepare graph director = QgsVectorLayerDirector( source=network_layer, directionFieldId=-1, directDirectionValue='', reverseDirectionValue='', bothDirectionValue='', defaultDirection=QgsVectorLayerDirector.DirectionBoth, ) # First strategy is for actual shortest distance calculation director.addStrategy(QgsNetworkDistanceStrategy()) # 0 # Second strategy is a hack to be able to recover the edge id director.addStrategy(SaveFidStrategy()) # 1 builder = QgsGraphBuilder(crs) ## spatial index poi_sidx = QgsSpatialIndex(poi_layer) work_sidx = QgsSpatialIndex(work_layer) school_sidx = QgsSpatialIndex(school_layer) ## prepare points orig_n = len(origin_layer) poi_n = len(poi_layer) work_n = len(work_layer) if work_layer else 0 school_n = len(school_layer) if school_layer else 0 dest_n = poi_n + work_n + school_n orig_points = [None] * orig_n orig_sizes = np.zeros(orig_n, dtype=float) if socio_data: orig_socio = np.zeros(orig_n, dtype=float) dest_points = [None] * dest_n dest_sizes = [None] * dest_n dest_fids = [None] * dest_n dest_cats = [None] * dest_n orig_id_field = 'deso' for i, feat in enumerate(origin_layer.getFeatures()): orig_points[i] = feat.geometry().asPoint() orig_sizes[i] = feat[size_field] * ( feat[origin_weight_field] if origin_weight_field else 1 ) if socio_data: orig_socio[i] = socio_data[ feat[orig_id_field] ] # FIXME: check if all origins have data if socio_data: orig_socio = orig_socio / np.mean(orig_socio) orig_sizes *= orig_socio for i, feat in enumerate(poi_layer.getFeatures()): dest_points[i] = feat.geometry().asPoint() dest_fids[i] = feat.id() dest_sizes[i] = 1 # TODO: dest size dest_cats[i] = poi_class_map.get(feat[class_field]) if work_layer: for i, feat in enumerate(work_layer.getFeatures(), start=poi_n): dest_points[i] = feat.geometry().asPoint() dest_fids[i] = feat.id() dest_sizes[i] = feat[work_size_field] # TODO: dest size dest_cats[i] = 'work' if school_layer: for i, feat in enumerate(school_layer.getFeatures(), start=(poi_n + work_n)): dest_points[i] = feat.geometry().asPoint() dest_fids[i] = feat.id() dest_sizes[i] = feat[school_size_field] # TODO: dest size dest_cats[i] = 'school' # points = [origin.point for origin in origins_data] + [ # dest.point for dest in dests_data # ] if feedback is None: feedback = QgsProcessingFeedback() def progress(p): if int(10 * p % 100) == 0: print(f'{int(p):#3d}%') feedback.progressChanged.connect(progress) with timing('build network graph'): tied_points = director.makeGraph( builder, orig_points + dest_points, feedback=feedback ) graph = builder.graph() orig_tied_points = tied_points[:orig_n] dest_tied_points = tied_points[orig_n:] poi_tied_points = dest_tied_points[:poi_n] work_tied_points = dest_tied_points[poi_n : poi_n + work_n] school_tied_points = dest_tied_points[poi_n + work_n :] dest_fid_to_tied_points = dict(zip(dest_fids, enumerate(dest_tied_points))) poi_fid_to_tied_points = dict(zip(dest_fids[:poi_n], enumerate(poi_tied_points))) work_fid_to_tied_points = dict( zip(dest_fids[poi_n : poi_n + work_n], enumerate(work_tied_points, start=poi_n)) ) school_fid_to_tied_points = dict( zip( dest_fids[poi_n + work_n :], enumerate(school_tied_points, start=poi_n + work_n), ) ) orig_dests = [None] * orig_n for i, point in enumerate(orig_points): orig_dests[i] = ( [ poi_fid_to_tied_points[fid] for fid in poi_sidx.nearestNeighbor( point, neighbors=MAX_NEIGHBORS, maxDistance=max_distance ) ] + [ work_fid_to_tied_points[fid] for fid in work_sidx.nearestNeighbor( point, neighbors=MAX_NEIGHBORS, maxDistance=max_distance ) ] + [ school_fid_to_tied_points[fid] for fid in school_sidx.nearestNeighbor( point, neighbors=MAX_NEIGHBORS, maxDistance=max_distance ) ] ) step = 100.0 / orig_n time_dijkstra = 0.0 time_find = 0.0 time_route = 0.0 with timing('calculate connecting routes'): routes = [] # for i, (origin_fid, dest_fids) in enumerate(od_data): for i, (orig_point, dests) in enumerate(zip(orig_tied_points, orig_dests)): origin_vertex_id = graph.findVertex(orig_point) # Calculate the tree and cost using the distance strategy (#0) ts = time() (tree, cost) = QgsGraphAnalyzer.dijkstra(graph, origin_vertex_id, 0) time_dijkstra += time() - ts for j, dest_point in dests: if feedback.isCanceled(): return if dest_sizes[j] <= 0: continue category = dest_cats[j] if category is None: continue ts = time() dest_vertex_id = graph.findVertex(dest_point) time_find += time() - ts if tree[dest_vertex_id] != -1 and ( cost[dest_vertex_id] <= MAX_DISTANCE_M or MAX_DISTANCE_M <= 0 # TODO: enable skipping max distance ): route_distance = cost[dest_vertex_id] # route_points = [graph.vertex(dest_vertex_id).point()] cur_vertex_id = dest_vertex_id route_fids = [] # Iterate the graph from dest to origin saving the edges ts = time() while cur_vertex_id != origin_vertex_id: cur_edge = graph.edge(tree[cur_vertex_id]) # Here we recover the edge id through strategy #1 route_fids.append(cur_edge.cost(1)) cur_vertex_id = cur_edge.fromVertex() # route_points.append(graph.vertex(cur_vertex_id).point()) time_route += time() - ts # route_points.reverse() # route_geom = QgsGeometry.fromPolylineXY(route_points)) # Hack to remove duplicate fids route_fids = list( dict.fromkeys(route_fids) ) # NOTE: requires python >= 3.7 for ordered dict FIXME: add python version check route_fids.reverse() # Calc # TODO: Move to matrix and vectorize calculation using numpy gravity_value = poi_gravity_values[category] bike_params = mode_params_bike[category] ebike_params = mode_params_ebike[category] # NOTE: we include dest size in decay here decay = dest_sizes[j] * math.exp( gravity_value * route_distance / 1000.0 ) p_bike = sigmoid(*bike_params, route_distance) p_ebike = sigmoid(*ebike_params, route_distance) # TODO: use namedtuple or dataclass routes.append( Route( i, j, category, route_distance, decay, p_bike, p_ebike, route_fids, ) ) feedback.setProgress(i * step) print(f'dijkstra took: {time_dijkstra:#1.2f} sec') print(f'find vertex took: {time_find:#1.2f} sec') print(f'route took: {time_route:#1.2f} sec') with timing('post process routes'): alpha_bike = 0.8 alpha_ebke = 0.2 decay_sums = {cat: defaultdict(float) for cat in poi_categories} bike_values = {cat: defaultdict(float) for cat in poi_categories} ebike_values = {cat: defaultdict(float) for cat in poi_categories} for route in routes: # NOTE: dest size is included in decay decay_sums[route.cat][route.i] += route.decay for route in routes: decay_sum = decay_sums[route.cat][route.i] # TODO: add T_p and alpha_m T_p = trip_generation[route.cat] bike_value = ( T_p * alpha_bike * orig_sizes[route.i] * route.p_bike * route.decay / decay_sum ) ebike_value = ( T_p * alpha_ebke * orig_sizes[route.i] * route.p_ebike * route.decay / decay_sum ) for fid in route.net_fids: bike_values[route.cat][fid] += float(bike_value) ebike_values[route.cat][fid] += float(ebike_value) # FIXME: Un-kludge this with timing('create result features'): fields = get_fields() segments = [] for feature in network_layer.getFeatures(): fid = feature.id() segment = QgsFeature(fields) segment.setGeometry(QgsGeometry(feature.geometry())) segment['network_fid'] = fid flow = 0.0 for cat in poi_categories: bike_field = f'{cat}_bike_value' ebike_field = f'{cat}_ebike_value' flow_bike = segment[bike_field] = bike_values[cat].get(fid) flow_ebke = segment[ebike_field] = ebike_values[cat].get(fid) if flow_bike is not None: flow += flow_bike if flow_ebke is not None: flow += flow_ebke segment['flow'] = flow segment['lts'] = feature['lts'] segment['vgu'] = feature['vgu'] segment['R'] = flow * feature['ratio'] segments.append(segment) if not return_layer: if return_raw: return segments, bike_values, ebike_values return segments with timing('create result layer'): output_layer = QgsVectorLayer( f'LineString?crs={crs.toWkt()}', 'segments', 'memory' ) with edit(output_layer): for field in fields: output_layer.addAttribute(field) output_layer.addFeatures(segments, flags=QgsFeatureSink.FastInsert) return output_layer
def handleAlgorithmResults(alg, context, feedback=None, showResults=True, parameters={}): wrongLayers = [] if feedback is None: feedback = QgsProcessingFeedback() feedback.setProgressText( QCoreApplication.translate('Postprocessing', 'Loading resulting layers')) i = 0 for l, details in context.layersToLoadOnCompletion().items(): if feedback.isCanceled(): return False if len(context.layersToLoadOnCompletion()) > 2: # only show progress feedback if we're loading a bunch of layers feedback.setProgress( 100 * i / float(len(context.layersToLoadOnCompletion()))) try: layer = QgsProcessingUtils.mapLayerFromString( l, context, typeHint=details.layerTypeHint) if layer is not None: set_layer_name(layer, details) '''If running a model, the execution will arrive here when an algorithm that is part of that model is executed. We check if its output is a final otuput of the model, and adapt the output name accordingly''' outputName = details.outputName expcontext = QgsExpressionContext() scope = QgsExpressionContextScope() expcontext.appendScope(scope) for out in alg.outputDefinitions(): if out.name() not in parameters: continue outValue = parameters[out.name()] if hasattr(outValue, "sink"): outValue = outValue.sink.valueAsString(expcontext)[0] else: outValue = str(outValue) if outValue == l: outputName = out.name() break style = None if outputName: style = RenderingStyles.getStyle(alg.id(), outputName) if style is None: if layer.type() == QgsMapLayerType.RasterLayer: style = ProcessingConfig.getSetting( ProcessingConfig.RASTER_STYLE) else: if layer.geometryType() == QgsWkbTypes.PointGeometry: style = ProcessingConfig.getSetting( ProcessingConfig.VECTOR_POINT_STYLE) elif layer.geometryType() == QgsWkbTypes.LineGeometry: style = ProcessingConfig.getSetting( ProcessingConfig.VECTOR_LINE_STYLE) else: style = ProcessingConfig.getSetting( ProcessingConfig.VECTOR_POLYGON_STYLE) if style: layer.loadNamedStyle(style) details.project.addMapLayer( context.temporaryLayerStore().takeMapLayer(layer)) if details.postProcessor(): details.postProcessor().postProcessLayer( layer, context, feedback) else: wrongLayers.append(str(l)) except Exception: QgsMessageLog.logMessage( QCoreApplication.translate( 'Postprocessing', "Error loading result layer:") + "\n" + traceback.format_exc(), 'Processing', Qgis.Critical) wrongLayers.append(str(l)) i += 1 feedback.setProgress(100) if wrongLayers: msg = QCoreApplication.translate( 'Postprocessing', "The following layers were not correctly generated.") msg += "<ul>" + "".join(["<li>%s</li>" % lay for lay in wrongLayers]) + "</ul>" msg += QCoreApplication.translate( 'Postprocessing', "You can check the 'Log Messages Panel' in QGIS main window to find more information about the execution of the algorithm." ) feedback.reportError(msg) return len(wrongLayers) == 0
def handleAlgorithmResults(alg, context, feedback=None, showResults=True): wrongLayers = [] if feedback is None: feedback = QgsProcessingFeedback() feedback.setProgressText(QCoreApplication.translate('Postprocessing', 'Loading resulting layers')) i = 0 for l, details in context.layersToLoadOnCompletion().items(): if feedback.isCanceled(): return False if len(context.layersToLoadOnCompletion()) > 2: # only show progress feedback if we're loading a bunch of layers feedback.setProgress(100 * i / float(len(context.layersToLoadOnCompletion()))) try: layer = QgsProcessingUtils.mapLayerFromString(l, context) if layer is not None: layer.setName(details.name) details.project.addMapLayer(context.temporaryLayerStore().takeMapLayer(layer)) else: name = details.name if ProcessingConfig.getSetting( ProcessingConfig.USE_FILENAME_AS_LAYER_NAME): name = os.path.basename(l) dataobjects.load(l, name, alg.crs, RenderingStyles.getStyle(alg.id(), l)) except Exception: QgsMessageLog.logMessage("Error loading result layer:\n" + traceback.format_exc(), 'Processing', QgsMessageLog.CRITICAL) #wrongLayers.append(out.description()) wrongLayers.append(l) # for out in alg.outputs: # feedback.setProgress(100 * i / float(len(alg.outputs))) # if out.flags() & QgsProcessingParameterDefinition.FlagHidden or not out.open: # continue # if isinstance(out, (OutputRaster, OutputVector, OutputTable)): # try: # layer = QgsProcessingUtils.mapLayerFromString(out.value, context) # if layer: # layer.setName(out.description) # QgsProject.instance().addMapLayer(context.temporaryLayerStore().takeMapLayer(layer)) # else: # if ProcessingConfig.getSetting( # ProcessingConfig.USE_FILENAME_AS_LAYER_NAME): # name = os.path.basename(out.value) # else: # name = out.description # # isRaster = True if isinstance(out, OutputRaster) else False # dataobjects.load(out.value, name, alg.crs, # RenderingStyles.getStyle(alg.id(), out.name), # isRaster) # except Exception: # QgsMessageLog.logMessage("Error loading result layer:\n" + traceback.format_exc(), 'Processing', QgsMessageLog.CRITICAL) # wrongLayers.append(out.description) # elif isinstance(out, OutputHTML): # resultsList.addResult(alg.icon(), out.description, out.value) i += 1 QApplication.restoreOverrideCursor() if wrongLayers: msg = "The following layers were not correctly generated.<ul>" msg += "".join(["<li>%s</li>" % lay for lay in wrongLayers]) + "</ul>" msg += "You can check the log messages to find more information about the execution of the algorithm" feedback.reportError(msg) return len(wrongLayers) == 0
def execute_in_place_run(alg, parameters, context=None, feedback=None, raise_exceptions=False): """Executes an algorithm modifying features in-place in the input layer. :param alg: algorithm to run :type alg: QgsProcessingAlgorithm :param parameters: parameters of the algorithm :type parameters: dict :param context: context, defaults to None :type context: QgsProcessingContext, optional :param feedback: feedback, defaults to None :type feedback: QgsProcessingFeedback, optional :param raise_exceptions: useful for testing, if True exceptions are raised, normally exceptions will be forwarded to the feedback :type raise_exceptions: boo, default to False :raises QgsProcessingException: raised when there is no active layer, or it cannot be made editable :return: a tuple with true if success and results :rtype: tuple """ if feedback is None: feedback = QgsProcessingFeedback() if context is None: context = dataobjects.createContext(feedback) # Only feature based algs have sourceFlags try: if alg.sourceFlags() & QgsProcessingFeatureSource.FlagSkipGeometryValidityChecks: context.setInvalidGeometryCheck(QgsFeatureRequest.GeometryNoCheck) except AttributeError: pass active_layer = parameters['INPUT'] # Run some checks and prepare the layer for in-place execution by: # - getting the active layer and checking that it is a vector # - making the layer editable if it was not already # - selecting all features if none was selected # - checking in-place support for the active layer/alg/parameters # If one of the check fails and raise_exceptions is True an exception # is raised, else the execution is aborted and the error reported in # the feedback try: if active_layer is None: raise QgsProcessingException(tr("There is not active layer.")) if not isinstance(active_layer, QgsVectorLayer): raise QgsProcessingException(tr("Active layer is not a vector layer.")) if not active_layer.isEditable(): if not active_layer.startEditing(): raise QgsProcessingException(tr("Active layer is not editable (and editing could not be turned on).")) if not alg.supportInPlaceEdit(active_layer): raise QgsProcessingException(tr("Selected algorithm and parameter configuration are not compatible with in-place modifications.")) except QgsProcessingException as e: if raise_exceptions: raise e QgsMessageLog.logMessage(str(sys.exc_info()[0]), 'Processing', Qgis.Critical) if feedback is not None: feedback.reportError(getattr(e, 'msg', str(e)), fatalError=True) return False, {} if not active_layer.selectedFeatureIds(): active_layer.selectAll() # Make sure we are working on selected features only parameters['INPUT'] = QgsProcessingFeatureSourceDefinition(active_layer.id(), True) parameters['OUTPUT'] = 'memory:' req = QgsFeatureRequest(QgsExpression(r"$id < 0")) req.setFlags(QgsFeatureRequest.NoGeometry) req.setSubsetOfAttributes([]) # Start the execution # If anything goes wrong and raise_exceptions is True an exception # is raised, else the execution is aborted and the error reported in # the feedback try: new_feature_ids = [] active_layer.beginEditCommand(alg.displayName()) # Checks whether the algorithm has a processFeature method if hasattr(alg, 'processFeature'): # in-place feature editing # Make a clone or it will crash the second time the dialog # is opened and run alg = alg.create() if not alg.prepare(parameters, context, feedback): raise QgsProcessingException(tr("Could not prepare selected algorithm.")) # Check again for compatibility after prepare if not alg.supportInPlaceEdit(active_layer): raise QgsProcessingException(tr("Selected algorithm and parameter configuration are not compatible with in-place modifications.")) field_idxs = range(len(active_layer.fields())) iterator_req = QgsFeatureRequest(active_layer.selectedFeatureIds()) iterator_req.setInvalidGeometryCheck(context.invalidGeometryCheck()) feature_iterator = active_layer.getFeatures(iterator_req) step = 100 / len(active_layer.selectedFeatureIds()) if active_layer.selectedFeatureIds() else 1 for current, f in enumerate(feature_iterator): feedback.setProgress(current * step) if feedback.isCanceled(): break # need a deep copy, because python processFeature implementations may return # a shallow copy from processFeature input_feature = QgsFeature(f) new_features = alg.processFeature(input_feature, context, feedback) new_features = QgsVectorLayerUtils.makeFeaturesCompatible(new_features, active_layer) if len(new_features) == 0: active_layer.deleteFeature(f.id()) elif len(new_features) == 1: new_f = new_features[0] if not f.geometry().equals(new_f.geometry()): active_layer.changeGeometry(f.id(), new_f.geometry()) if f.attributes() != new_f.attributes(): active_layer.changeAttributeValues(f.id(), dict(zip(field_idxs, new_f.attributes())), dict(zip(field_idxs, f.attributes()))) new_feature_ids.append(f.id()) else: active_layer.deleteFeature(f.id()) # Get the new ids old_ids = set([f.id() for f in active_layer.getFeatures(req)]) if not active_layer.addFeatures(new_features): raise QgsProcessingException(tr("Error adding processed features back into the layer.")) new_ids = set([f.id() for f in active_layer.getFeatures(req)]) new_feature_ids += list(new_ids - old_ids) results, ok = {}, True else: # Traditional 'run' with delete and add features cycle # There is no way to know if some features have been skipped # due to invalid geometries if context.invalidGeometryCheck() == QgsFeatureRequest.GeometrySkipInvalid: selected_ids = active_layer.selectedFeatureIds() else: selected_ids = [] results, ok = alg.run(parameters, context, feedback) if ok: result_layer = QgsProcessingUtils.mapLayerFromString(results['OUTPUT'], context) # TODO: check if features have changed before delete/add cycle new_features = [] # Check if there are any skipped features if context.invalidGeometryCheck() == QgsFeatureRequest.GeometrySkipInvalid: missing_ids = list(set(selected_ids) - set(result_layer.allFeatureIds())) if missing_ids: for f in active_layer.getFeatures(QgsFeatureRequest(missing_ids)): if not f.geometry().isGeosValid(): new_features.append(f) active_layer.deleteFeatures(active_layer.selectedFeatureIds()) for f in result_layer.getFeatures(): new_features.extend(QgsVectorLayerUtils. makeFeaturesCompatible([f], active_layer)) # Get the new ids old_ids = set([f.id() for f in active_layer.getFeatures(req)]) if not active_layer.addFeatures(new_features): raise QgsProcessingException(tr("Error adding processed features back into the layer.")) new_ids = set([f.id() for f in active_layer.getFeatures(req)]) new_feature_ids += list(new_ids - old_ids) active_layer.endEditCommand() if ok and new_feature_ids: active_layer.selectByIds(new_feature_ids) elif not ok: active_layer.rollBack() return ok, results except QgsProcessingException as e: active_layer.endEditCommand() active_layer.rollBack() if raise_exceptions: raise e QgsMessageLog.logMessage(str(sys.exc_info()[0]), 'Processing', Qgis.Critical) if feedback is not None: feedback.reportError(getattr(e, 'msg', str(e)), fatalError=True) return False, {}
def runGdal(commands, feedback=None): if feedback is None: feedback = QgsProcessingFeedback() envval = os.getenv('PATH') # We need to give some extra hints to get things picked up on OS X isDarwin = False try: isDarwin = platform.system() == 'Darwin' except IOError: # https://travis-ci.org/m-kuhn/QGIS#L1493-L1526 pass if isDarwin and os.path.isfile(os.path.join(QgsApplication.prefixPath(), "bin", "gdalinfo")): # Looks like there's a bundled gdal. Let's use it. os.environ['PATH'] = "{}{}{}".format(os.path.join(QgsApplication.prefixPath(), "bin"), os.pathsep, envval) os.environ['DYLD_LIBRARY_PATH'] = os.path.join(QgsApplication.prefixPath(), "lib") else: # Other platforms should use default gdal finder codepath settings = QgsSettings() path = settings.value('/GdalTools/gdalPath', '') if not path.lower() in envval.lower().split(os.pathsep): envval += '{}{}'.format(os.pathsep, path) os.putenv('PATH', envval) fused_command = ' '.join([str(c) for c in commands]) QgsMessageLog.logMessage(fused_command, 'Processing', Qgis.Info) feedback.pushInfo(GdalUtils.tr('GDAL command:')) feedback.pushCommandInfo(fused_command) feedback.pushInfo(GdalUtils.tr('GDAL command output:')) loglines = [GdalUtils.tr('GDAL execution console output')] def on_stdout(ba): val = ba.data().decode('UTF-8') # catch progress reports if val == '100 - done.': on_stdout.progress = 100 feedback.setProgress(on_stdout.progress) elif val in ('0', '10', '20', '30', '40', '50', '60', '70', '80', '90'): on_stdout.progress = int(val) feedback.setProgress(on_stdout.progress) elif val == '.': on_stdout.progress += 2.5 feedback.setProgress(on_stdout.progress) on_stdout.buffer += val if on_stdout.buffer.endswith('\n') or on_stdout.buffer.endswith('\r'): # flush buffer feedback.pushConsoleInfo(on_stdout.buffer.rstrip()) loglines.append(on_stdout.buffer.rstrip()) on_stdout.buffer = '' on_stdout.progress = 0 on_stdout.buffer = '' def on_stderr(ba): val = ba.data().decode('UTF-8') on_stderr.buffer += val if on_stderr.buffer.endswith('\n') or on_stderr.buffer.endswith('\r'): # flush buffer feedback.reportError(on_stderr.buffer.rstrip()) loglines.append(on_stderr.buffer.rstrip()) on_stderr.buffer = '' on_stderr.buffer = '' command, *arguments = QgsRunProcess.splitCommand(fused_command) proc = QgsBlockingProcess(command, arguments) proc.setStdOutHandler(on_stdout) proc.setStdErrHandler(on_stderr) res = proc.run(feedback) if feedback.isCanceled() and res != 0: feedback.pushInfo(GdalUtils.tr('Process was canceled and did not complete')) elif not feedback.isCanceled() and proc.exitStatus() == QProcess.CrashExit: raise QgsProcessingException(GdalUtils.tr('Process was unexpectedly terminated')) elif res == 0: feedback.pushInfo(GdalUtils.tr('Process completed successfully')) elif proc.processError() == QProcess.FailedToStart: raise QgsProcessingException(GdalUtils.tr('Process {} failed to start. Either {} is missing, or you may have insufficient permissions to run the program.').format(command, command)) else: feedback.reportError(GdalUtils.tr('Process returned error code {}').format(res)) return loglines
def mergeDataSources2Vrt(self, dataSources, outFile, union=False, relative=False, schema=False, feedback=None): '''Function to do the work of merging datasources in a single vrt format @param data_sources: Array of path strings @param outfile: the output vrt file to generate @param relative: Write relative flag. DOES NOT relativise paths. They have to be already relative @param schema: Schema flag @return: vrt in string format ''' if feedback is None: feedback = QgsProcessingFeedback() vrt = '<OGRVRTDataSource>' if union: vrt += '<OGRVRTUnionLayer name="UnionedLayer">' total = 100.0 / len(dataSources) if dataSources else 1 for current, layer in enumerate(dataSources): if feedback.isCanceled(): break feedback.setProgress(int(current * total)) inFile = layer.source() srcDS = ogr.Open(inFile, 0) if srcDS is None: raise QgsProcessingException( self.tr('Invalid datasource: {}'.format(inFile))) if schema: inFile = '@dummy@' for layer in srcDS: layerDef = layer.GetLayerDefn() layerName = layerDef.GetName() vrt += '<OGRVRTLayer name="{}">'.format(self.XmlEsc(layerName)) vrt += '<SrcDataSource relativeToVRT="{}" shared="{}">{}</SrcDataSource>'.format( 1 if relative else 0, not schema, self.XmlEsc(inFile)) if schema: vrt += '<SrcLayer>@dummy@</SrcLayer>' else: vrt += '<SrcLayer>{}</SrcLayer>'.format( self.XmlEsc(layerName)) vrt += '<GeometryType>{}</GeometryType>'.format( self.GeomType2Name(layerDef.GetGeomType())) crs = layer.GetSpatialRef() if crs is not None: vrt += '<LayerSRS>{}</LayerSRS>'.format( self.XmlEsc(crs.ExportToWkt())) # Process all the fields. for fieldIdx in range(layerDef.GetFieldCount()): fieldDef = layerDef.GetFieldDefn(fieldIdx) vrt += '<Field name="{}" type="{}"'.format( self.XmlEsc(fieldDef.GetName()), self.fieldType2Name(fieldDef.GetType())) if not schema: vrt += ' src="{}"'.format( self.XmlEsc(fieldDef.GetName())) if fieldDef.GetWidth() > 0: vrt += ' width="{}"'.format(fieldDef.GetWidth()) if fieldDef.GetPrecision() > 0: vrt += ' precision="{}"'.format( fieldDef.GetPrecision()) vrt += '/>' vrt += '</OGRVRTLayer>' srcDS.Destroy() if union: vrt += '</OGRVRTUnionLayer>' vrt += '</OGRVRTDataSource>' #TODO: pretty-print XML if outFile is not None: with codecs.open(outFile, 'w') as f: f.write(vrt) return vrt
def processAlgorithm( self, parameters: Dict[str, Any], context: QgsProcessingContext, feedback: QgsProcessingFeedback) -> Union[dict, Dict[str, Any]]: # Словарь в котором будут сохраняться результаты работы алгоритма result = dict() # Получаем переданные на вход параметры source: QgsFeatureSource = self.parameterAsSource( parameters, self.SAMPLES, context) if self.MOSAIC in parameters and parameters[self.MOSAIC]: whole_mosaic: Optional[ QgsRasterLayer] = self.parameterAsRasterLayer( parameters, self.MOSAIC, context) else: whole_mosaic = None horresolution: float = self.parameterAsDouble(parameters, self.HORRESOLUTION, context) vertresolution: float = self.parameterAsDouble(parameters, self.VERTRESOLUTION, context) width: int = self.parameterAsInt(parameters, self.WIDTH, context) height: int = self.parameterAsInt(parameters, self.HEIGHT, context) dest_crs: QgsCoordinateReferenceSystem = self.parameterAsCrs( parameters, self.CRS, context) if self.FOLDER in parameters and parameters[self.FOLDER]: folder: Optional[str] = self.parameterAsFileOutput( parameters, self.FOLDER, context) else: folder = None saveonesample: bool = self.parameterAsBoolean(parameters, self.SAVEONESAMPLE, context) step = 0 model_feedback = QgsProcessingMultiStepFeedback(4, feedback) if feedback.isCanceled(): return result # Запускаем загруженный из файла алгоритм, который создает слои INTERSECTION и GRID temp_grid_id = processing.run( self.grid_model.model(), { 'CRS': dest_crs, 'SAMPLES': parameters[self.SAMPLES], 'HORRESOLUTION': horresolution, 'VERTRESOLUTION': vertresolution, 'HEIGHT': height, 'WIDTH': width, 'VERBOSE_LOG': True, 'native:renametablefield_1:{}'.format(self.GRID): 'TEMPORARY_OUTPUT' }, context=context, feedback=model_feedback, is_child_algorithm=True)['native:renametablefield_1:{}'.format( self.GRID)] step += 1 model_feedback.setCurrentStep(step) if feedback.isCanceled(): return result temp_grid: QgsVectorLayer = context.takeResultLayer(temp_grid_id) if whole_mosaic and folder: # Извлекаем екстент из мозаики и любую (первую) точку из грида, чтобы по ним вычислить смещение mosaic_extent: QgsRectangle = whole_mosaic.extent() first_point: QgsPoint = temp_grid.getFeatures().__next__( ).geometry().vertices().__next__() # вычисляем смещения и двигаем грид translated_grid_id = processing.run( "native:translategeometry", { 'INPUT': temp_grid, 'DELTA_X': (mosaic_extent.xMaximum() - first_point.x()) % horresolution, 'DELTA_Y': (mosaic_extent.yMaximum() - first_point.y()) % vertresolution, 'DELTA_Z': 0, 'DELTA_M': 0, 'OUTPUT': 'TEMPORARY_OUTPUT' }, context=context, feedback=model_feedback, is_child_algorithm=True)['OUTPUT'] translated_grid: QgsVectorLayer = context.takeResultLayer( translated_grid_id) else: translated_grid_id = temp_grid_id translated_grid = temp_grid step += 1 model_feedback.setCurrentStep(step) if feedback.isCanceled(): return result temp_intersection_id = processing.run( self.intersection_model.model(), { 'CRS': dest_crs, 'SAMPLES': parameters[self.SAMPLES], 'GRID': translated_grid, 'VERBOSE_LOG': True, 'native:intersection_1:{}'.format(self.INTERSECTION): 'TEMPORARY_OUTPUT' }, context=context, feedback=model_feedback, is_child_algorithm=True)['native:intersection_1:{}'.format( self.INTERSECTION)] temp_intersection: QgsVectorLayer = context.takeResultLayer( temp_intersection_id) # Создаем выходные слои, записываем в них features и сохраняем с словарь результатов result (intersection, intersection_id) = self.parameterAsSink(parameters, self.INTERSECTION, context, temp_intersection.fields(), temp_intersection.wkbType(), temp_intersection.sourceCrs()) (grid, grid_id) = self.parameterAsSink(parameters, self.GRID, context, translated_grid.fields(), translated_grid.wkbType(), translated_grid.sourceCrs()) intersection.addFeatures(temp_intersection.getFeatures()) grid.addFeatures(translated_grid.getFeatures()) result.update({self.INTERSECTION: intersection_id, self.GRID: grid_id}) step += 1 model_feedback.setCurrentStep(step) if feedback.isCanceled(): return result # Поля для выходного слоя RLE rle_fields = QgsFields() rle_fields.append(QgsField("Image_Label", QVariant.String)) rle_fields.append(QgsField("EncodedPixels", QVariant.String)) # Выходной слой RLE (rle, rle_id) = self.parameterAsSink(parameters, self.RLE, context, rle_fields, translated_grid.wkbType(), QgsCoordinateReferenceSystem()) rle: QgsFeatureSink store: QgsMapLayerStore = QgsProject.instance().layerStore() # Добавляем слои в хранилище слоев проекта, чтобы их id видел QgsProcessingFeatureSourceDefinition store.addMapLayer(temp_intersection) store.addMapLayer(translated_grid) # Индикатор для сохранения одного растра с sample first = True # Цикл по каждому тайлу из GRID for current, tile_feat in enumerate(translated_grid.getFeatures()): tile_feat: QgsFeature if feedback.isCanceled(): return result # для каждого тайла создаем запрос по которому будут запрошены соответствующие feature из INTERSECTION expression = QgsExpression().createFieldEqualityExpression( "tile_id", tile_feat["tile_id"]) request = QgsFeatureRequest() request.setFilterExpression(expression) # Цикл по всем feature в INTERSECTION for samle_feat in temp_intersection.getFeatures(request): samle_feat: QgsFeature # выделяем в слое INTERSECTION текущую feature, чтобы только ее передать в алгоритм обрезки растра temp_intersection.selectByIds([samle_feat.id()]) # Превращаем текущую feature из INTERSECTION в растр, экстент растра задаем по текущему тайлу bin_raster = processing.run("gdal:rasterize", { 'BURN': 1, 'DATA_TYPE': 0, 'EXTENT': tile_feat.geometry().boundingBox(), 'EXTRA': '', 'FIELD': '', 'HEIGHT': vertresolution, 'INIT': None, 'INPUT': QgsProcessingFeatureSourceDefinition( temp_intersection.id(), selectedFeaturesOnly=True), 'INVERT': False, 'NODATA': 0, 'OPTIONS': 'NBITS=1', 'OUTPUT': 'TEMPORARY_OUTPUT', 'UNITS': 1, 'WIDTH': horresolution }, context=context, feedback=model_feedback, is_child_algorithm=True)['OUTPUT'] # Получаем результат работы алгоритма в виде растра bin_raster: QgsRasterLayer = QgsProcessingUtils.mapLayerFromString( bin_raster, context) if first and folder and saveonesample: first = False save_raster( bin_raster, folder, str(tile_feat["tile_id"]).zfill(5) + '_sample.tif') # Получаем строку RLE raster_rle_string = rle_encode( convertRasterToNumpyArray(bin_raster)) # Записываем строку с описанием RLE в слой RLE feat = QgsFeature(rle_fields) feat["Image_Label"] = tile_feat["tile_id"] feat["EncodedPixels"] = raster_rle_string rle.addFeatures([feat]) if feedback.isCanceled(): return result # если указаны мозаика и выходная папка, то обрезаем мозаику по текущему тайлу GRID if whole_mosaic and folder: if feedback.isCanceled(): return result # выделяем в слое GRID текущий тайл, чтобы только его передать в алгоритм обрезки растра translated_grid.selectByIds([tile_feat.id()]) mosaic_tile_temp_raster = processing.run( "gdal:cliprasterbymasklayer", { 'ALPHA_BAND': False, 'CROP_TO_CUTLINE': True, 'DATA_TYPE': 0, 'EXTRA': '', 'INPUT': whole_mosaic, 'KEEP_RESOLUTION': False, # 'MASK': single_grid, 'MASK': QgsProcessingFeatureSourceDefinition( translated_grid.id(), selectedFeaturesOnly=True), 'MULTITHREADING': False, 'NODATA': None, 'OPTIONS': '', # 'OUTPUT': 'TEMPORARY_OUTPUT', 'OUTPUT': os.path.join(folder, str(tile_feat["tile_id"]).zfill(5)) + '.tif', 'SET_RESOLUTION': True, 'SOURCE_CRS': None, 'TARGET_CRS': None, 'X_RESOLUTION': abs(whole_mosaic.rasterUnitsPerPixelX()), 'Y_RESOLUTION': abs(whole_mosaic.rasterUnitsPerPixelX()) }, context=context, feedback=model_feedback, is_child_algorithm=True)["OUTPUT"] if feedback.isCanceled(): return result store.removeMapLayer(temp_intersection) store.removeMapLayer(translated_grid) step += 1 model_feedback.setCurrentStep(step) if feedback.isCanceled(): return result result.update({self.RLE: rle_id}) return result