def loadRasterLayer(self, name, layer, external=True, band=1, destName=None): """ Creates a dedicated command to load a raster into the temporary GRASS DB. :param name: name of the parameter. :param layer: QgsMapLayer for the raster layer. :param external: True if using r.external. :param band: imports only specified band. None for all bands. :param destName: force the destination name of the raster. """ self.inputLayers.append(layer) self.setSessionProjectionFromLayer(layer) if not destName: destName = 'rast_{}'.format(os.path.basename(getTempFilename())) self.exportedLayers[name] = destName command = '{0} input="{1}" {2}output="{3}" --overwrite -o'.format( 'r.external' if external else 'r.in.gdal', os.path.normpath(layer.source()), 'band={} '.format(band) if band else '', destName) self.commands.append(command)
def loadVectorLayer(self, name, layer, external=False): """ Creates a dedicated command to load a vector into temporary GRASS DB. :param name: name of the parameter :param layer: QgsMapLayer for the vector layer. :param external: use v.external (v.in.ogr if False). """ # TODO: support multiple input formats if external is None: external = ProcessingConfig.getSetting( Grass7Utils.GRASS_USE_VEXTERNAL) # safety check: we can only use external for ogr layers which support random read if external: ds = ogr.Open(layer.source()) if ds is not None: ogr_layer = ds.GetLayer() if ogr_layer is None or not ogr_layer.TestCapability( ogr.OLCRandomRead): external = False else: external = False self.inputLayers.append(layer) self.setSessionProjectionFromLayer(layer) destFilename = 'vector_{}'.format(os.path.basename(getTempFilename())) self.exportedLayers[name] = destFilename command = '{0}{1}{2} input="{3}" output="{4}" --overwrite -o'.format( 'v.external' if external else 'v.in.ogr', ' min_area={}'.format(self.minArea) if not external else '', ' snap={}'.format(self.snapTolerance) if not external else '', os.path.normpath(layer.source()), destFilename) self.commands.append(command)
def processAlgorithm(self, parameters, context, feedback): # TODO: check correct num of bands inLayer = self.parameterAsRasterLayer(parameters, self.INPUT, context) input = inLayer.source() temp = getTempFilename(None).replace('.', '') basename = os.path.basename(temp) validChars = \ 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' safeBasename = ''.join(c for c in basename if c in validChars) temp = os.path.join(os.path.dirname(temp), safeBasename) r = self.parameterAsOutputLayer(parameters, self.R, context) g = self.parameterAsOutputLayer(parameters, self.G, context) b = self.parameterAsOutputLayer(parameters, self.B, context) commands = [] version = SagaUtils.getInstalledVersion(True) trailing = "" lib = "" commands.append('%sio_gdal 0 -GRIDS "%s" -FILES "%s"' % (lib, temp, input)) commands.append( '%sio_gdal 1 -GRIDS "%s_%s1.sgrd" -FORMAT 1 -TYPE 0 -FILE "%s"' % (lib, temp, trailing, r)) commands.append( '%sio_gdal 1 -GRIDS "%s_%s2.sgrd" -FORMAT 1 -TYPE 0 -FILE "%s"' % (lib, temp, trailing, g)) commands.append( '%sio_gdal 1 -GRIDS "%s_%s3.sgrd" -FORMAT 1 -TYPE 0 -FILE "%s"' % (lib, temp, trailing, b)) SagaUtils.createSagaBatchJobFileFromSagaCommands(commands) SagaUtils.executeSaga(feedback) return {self.R: r, self.G: g, self.B: b}
def orderedInput(alg, parameters, context, src, tgt, numSeq=None): """Import multiple rasters in order :param alg: algorithm object. :param parameters: algorithm parameters dict. :param context: algorithm context. :param src: Name of the source parameter. :param tgt: Name of a new input parameter. :param numSeq: List of a sequence for naming layers. """ rootFilename = 'rast_{}.'.format(os.path.basename(getTempFilename())) #parameters[tgt] = rootFilename param = QgsProcessingParameterString(tgt, 'virtual input', rootFilename, False, False) alg.addParameter(param) rasters = alg.parameterAsLayerList(parameters, src, context) # Handle specific range if numSeq is None: numSeq = list(range(1, len(rasters) + 1)) for idx, raster in enumerate(rasters): rasterName = '{}{}'.format(rootFilename, numSeq[idx]) alg.loadRasterLayer(rasterName, raster, False, None, rasterName) # Don't forget to remove the old input parameter alg.removeParameter(src)
def processCommand(alg, parameters, context, feedback): # Temporary remove outputs and add a virtual output parameter outputName = 'output_{}'.format(os.path.basename(getTempFilename())) param = QgsProcessingParameterString('output', 'virtual output', outputName, False, False) alg.addParameter(param) alg.processCommand(parameters, context, feedback, True)
def processCommand(alg, parameters, context, feedback): """ Handle data preparation for v.net.distance: * Integrate point layers into network vector map. * Make v.net.distance use those layers. * Delete the threshold parameter. * If where statement, connect to the db """ # Grab the point layer and delete this parameter lineLayer = alg.exportedLayers['input'] fromLayer = alg.exportedLayers['flayer'] toLayer = alg.exportedLayers['tlayer'] intLayer = 'bufnet' + os.path.basename(getTempFilename()) netLayer = 'net' + os.path.basename(getTempFilename()) threshold = alg.parameterAsDouble(parameters, 'threshold', context) # Create the v.net connect command for from_layer integration command = u"v.net input={} points={} output={} operation=connect threshold={} arc_layer=1 node_layer=2".format( lineLayer, fromLayer, intLayer, threshold) alg.commands.append(command) # Do it again with to_layer command = u"v.net input={} points={} output={} operation=connect threshold={} arc_layer=1 node_layer=3".format( intLayer, toLayer, netLayer, threshold) alg.commands.append(command) # Connect the point layer database to the layer 2 of the network command = u"v.db.connect -o map={} table={} layer=2".format( netLayer, fromLayer) alg.commands.append(command) command = u"v.db.connect -o map={} table={} layer=3".format( netLayer, toLayer) alg.commands.append(command) # remove undesired parameters alg.removeParameter('flayer') alg.removeParameter('tlayer') alg.removeParameter('threshold') alg.exportedLayers['input'] = netLayer # Add the two new parameters fLayer = QgsProcessingParameterString('from_layer', None, 2, False, False) alg.addParameter(fLayer) tLayer = QgsProcessingParameterString('to_layer', None, 3, False, False) alg.addParameter(tLayer) alg.processCommand(parameters, context, feedback)
def configFile(alg, parameters, context, feedback, outputTxt=False): """Handle inline configuration :param parameters: """ # Where is the GRASS7 user directory ? userGrass7Path = rliPath() if not os.path.isdir(userGrass7Path): mkdir(userGrass7Path) if not os.path.isdir(os.path.join(userGrass7Path, 'output')): mkdir(os.path.join(userGrass7Path, 'output')) # If we have a configuration file, we need to copy it into user dir if parameters['config']: fileName = alg.parameterAsString(parameters, 'config', context) configFilePath = os.path.join(userGrass7Path, os.path.basename(fileName)) # Copy the file shutil.copy(parameters['config'], configFilePath) # Change the parameter value parameters['config'] = os.path.basename(configFilePath) # Handle inline configuration elif parameters['config_txt']: # Creates a temporary txt file in user r.li directory tempConfig = os.path.basename(getTempFilename()) configFilePath = os.path.join(userGrass7Path, tempConfig) # Inject rules into temporary txt file with open(configFilePath, "w") as f: f.write(alg.parameterAsString(parameters, 'config_txt', context)) f.write("\n") # Use temporary file as rules file parameters['config'] = os.path.basename(configFilePath) alg.removeParameter('config_txt') # For ascii output, we need a virtual output if outputTxt: param = QgsProcessingParameterString( 'output', 'virtual output', 'a' + os.path.basename(getTempFilename()), False, False) alg.addParameter(param) alg.processCommand(parameters, context, feedback, outputTxt) # Remove Config file: removeConfigFile(alg, parameters, context)
def processCommand(alg, parameters, context, feedback): # Handle inline rules txtRules = alg.parameterAsString(parameters, 'txtrules', context) if txtRules: # Creates a temporary txt file tempRulesName = getTempFilename() # Inject rules into temporary txt file with open(tempRulesName, "w") as tempRules: tempRules.write(txtRules) alg.removeParameter('txtrules') parameters['rules'] = tempRulesName alg.processCommand(parameters, context, feedback, True)
def processCommand(alg, parameters, context, feedback): # handle inline points inlinePoints = alg.parameterAsString(parameters, 'inline_points', context) if inlinePoints: # Creates a temporary txt file pointsName = getTempFilename() # Inject rules into temporary txt file with open(pointsName, "w") as tempPoints: tempPoints.write(inlinePoints) alg.removeParameter('inline_points') parameters['points'] = tempPoints alg.processCommand(parameters, context, feedback, True)
def loadAttributeTable(self, name, layer, destName=None): """ Creates a dedicated command to load an attribute table into the temporary GRASS DB. :param name: name of the input parameter. :param layer: a layer object to import from. :param destName: force the name for the table into GRASS DB. """ self.inputLayers.append(layer) if not destName: destName = 'table_{}'.format(os.path.basename(getTempFilename())) self.exportedLayers[name] = destName command = 'db.in.ogr --overwrite input="{0}" output="{1}"'.format( os.path.normpath(layer.source()), destName) self.commands.append(command)
def incorporatePoints(alg, parameters, context, feedback, pointLayerName='points', networkLayerName='input'): """ incorporate points with lines to form a GRASS network """ # Grab the point layer and delete this parameter pointLayer = alg.parameterAsVectorLayer(parameters, pointLayerName, context) if pointLayer: # Create an intermediate GRASS layer which is the combination of network + centers intLayer = 'net' + os.path.basename(getTempFilename()) pointLayer = alg.exportedLayers[pointLayerName] # Grab the network layer lineLayer = alg.parameterAsVectorLayer(parameters, networkLayerName, context) if lineLayer: lineLayer = alg.exportedLayers[networkLayerName] else: raise QgsProcessingException( alg.tr('GRASS GIS 7 v.net requires a lines layer!')) threshold = alg.parameterAsDouble(parameters, 'threshold', context) # Create the v.net connect command for point layer integration command = u"v.net input={} points={} output={} operation=connect threshold={}".format( lineLayer, pointLayer, intLayer, threshold) alg.commands.append(command) # Connect the point layer database to the layer 2 of the network command = u"v.db.connect -o map={} table={} layer=2".format(intLayer, pointLayer) alg.commands.append(command) # remove undesired parameters alg.removeParameter(pointLayerName) # Use temp layer for input alg.exportedLayers[networkLayerName] = intLayer # Process the command if 'threshold' in parameters: alg.removeParameter('threshold') alg.processCommand(parameters, context, feedback)
def regroupRasters(alg, parameters, context, src, group, subgroup=None, extFile=None): """ Group multiple input rasters into a group * If there is a subgroupField, a subgroup will automatically be created. * When an external file is provided, the file is copied into the respective directory of the subgroup. :param parameters: :param context: :param src: name of input parameter with multiple rasters. :param group: name of group. :param subgroup: name of subgroup. :param extFile: dict : parameterName:directory name """ # Create a group parameter groupName = 'group_{}'.format(os.path.basename(getTempFilename())) param = QgsProcessingParameterString(group, 'virtual group', groupName, False, False) alg.addParameter(param) # Create a subgroup subgroupName = None if subgroup: subgroupName = 'subgroup_{}'.format(os.path.basename( getTempFilename())) param = QgsProcessingParameterString(subgroup, 'virtual subgroup', subgroupName, False, False) alg.addParameter(param) # Compute raster names rasters = alg.parameterAsLayerList(parameters, src, context) rasterNames = [] for idx, raster in enumerate(rasters): name = '{}_{}'.format(src, idx) if name in alg.exportedLayers: rasterNames.append(alg.exportedLayers[name]) # Insert a i.group command command = 'i.group group={}{} input={}'.format( groupName, ' subgroup={}'.format(subgroupName) if subgroup else '', ','.join(rasterNames)) alg.commands.append(command) # Handle external files # if subgroupField and extFile: # for ext in extFile.keys(): # extFileName = new_parameters[ext] # if extFileName: # shortFileName = os.path.basename(extFileName) # destPath = os.path.join(Grass7Utils.grassMapsetFolder(), # 'PERMANENT', # 'group', new_parameters[group.name()], # 'subgroup', new_parameters[subgroup.name()], # extFile[ext], shortFileName) # copyFile(alg, extFileName, destPath) alg.removeParameter(src) return groupName, subgroupName
def processAlgorithm(self, parameters, context, feedback): commands = list() self.exportedLayers = {} self.preProcessInputs() extent = None crs = None # 1: Export rasters to sgrd and vectors to shp # Tables must be in dbf format. We check that. for param in self.parameterDefinitions(): if isinstance(param, QgsProcessingParameterRasterLayer): if param.name( ) not in parameters or parameters[param.name()] is None: continue if isinstance(parameters[param.name()], str): if parameters[param.name()].lower().endswith('sdat'): self.exportedLayers[param.name( )] = parameters[param.name()][:-4] + 'sgrd' if parameters[param.name()].lower().endswith('sgrd'): self.exportedLayers[param.name()] = parameters[ param.name()] else: layer = self.parameterAsRasterLayer( parameters, param.name(), context) exportCommand = self.exportRasterLayer( param.name(), layer) if exportCommand is not None: commands.append(exportCommand) else: if parameters[param.name()].source().lower().endswith( 'sdat'): self.exportedLayers[param.name( )] = parameters[param.name()].source()[:-4] + 'sgrd' if parameters[param.name()].source().lower().endswith( 'sgrd'): self.exportedLayers[param.name()] = parameters[ param.name()].source() else: exportCommand = self.exportRasterLayer( param.name(), parameters[param.name()]) if exportCommand is not None: commands.append(exportCommand) elif isinstance(param, QgsProcessingParameterFeatureSource): if param.name( ) not in parameters or parameters[param.name()] is None: continue if not crs: source = self.parameterAsSource(parameters, param.name(), context) crs = source.sourceCrs() layer_path = self.parameterAsCompatibleSourceLayerPath( parameters, param.name(), context, ['shp'], 'shp', feedback=feedback) if layer_path: self.exportedLayers[param.name()] = layer_path else: raise QgsProcessingException( self.tr('Unsupported file format')) elif isinstance(param, QgsProcessingParameterMultipleLayers): if param.name( ) not in parameters or parameters[param.name()] is None: continue layers = self.parameterAsLayerList(parameters, param.name(), context) if layers is None or len(layers) == 0: continue if param.layerType() == QgsProcessing.TypeRaster: files = [] for i, layer in enumerate(layers): if layer.source().lower().endswith('sdat'): files.append( parameters[param.name()].source()[:-4] + 'sgrd') if layer.source().lower().endswith('sgrd'): files.append(parameters[param.name()].source()) else: exportCommand = self.exportRasterLayer( param.name(), layer) files.append(self.exportedLayers[param.name()]) if exportCommand is not None: commands.append(exportCommand) self.exportedLayers[param.name()] = files else: temp_params = deepcopy(parameters) for layer in layers: temp_params[param.name()] = layer if not crs: source = self.parameterAsSource( temp_params, param.name(), context) crs = source.sourceCrs() layer_path = self.parameterAsCompatibleSourceLayerPath( temp_params, param.name(), context, 'shp', feedback=feedback) if layer_path: if param.name() in self.exportedLayers: self.exportedLayers[param.name()].append( layer_path) else: self.exportedLayers[param.name()] = [ layer_path ] else: raise QgsProcessingException( self.tr('Unsupported file format')) # 2: Set parameters and outputs command = self.undecorated_group + ' "' + self.cmdname + '"' command += ' ' + ' '.join(self.hardcoded_strings) for param in self.parameterDefinitions(): if not param.name() in parameters or parameters[ param.name()] is None: continue if param.isDestination(): continue if isinstance(param, (QgsProcessingParameterRasterLayer, QgsProcessingParameterFeatureSource)): command += ' -{} "{}"'.format( param.name(), self.exportedLayers[param.name()]) elif isinstance(param, QgsProcessingParameterMultipleLayers): command += ' -{} "{}"'.format( param.name(), ';'.join(self.exportedLayers[param.name()])) elif isinstance(param, QgsProcessingParameterBoolean): if self.parameterAsBool(parameters, param.name(), context): command += ' -{} true'.format(param.name().strip()) else: command += ' -{} false'.format(param.name().strip()) elif isinstance(param, QgsProcessingParameterMatrix): tempTableFile = getTempFilename('txt') with open(tempTableFile, 'w') as f: f.write('\t'.join([col for col in param.headers()]) + '\n') values = self.parameterAsMatrix(parameters, param.name(), context) for i in range(0, len(values), 3): s = values[i] + '\t' + values[i + 1] + '\t' + values[ i + 2] + '\n' f.write(s) command += ' -{} "{}"'.format(param.name(), tempTableFile) elif isinstance(param, QgsProcessingParameterExtent): # 'We have to substract/add half cell size, since SAGA is # center based, not corner based halfcell = self.getOutputCellsize(parameters, context) / 2 offset = [halfcell, -halfcell, halfcell, -halfcell] rect = self.parameterAsExtent(parameters, param.name(), context) values = [] values.append(rect.xMinimum()) values.append(rect.xMaximum()) values.append(rect.yMinimum()) values.append(rect.yMaximum()) for i in range(4): command += ' -{} {}'.format(self.extentParamNames[i], float(values[i]) + offset[i]) elif isinstance(param, QgsProcessingParameterNumber): if param.dataType() == QgsProcessingParameterNumber.Integer: command += ' -{} {}'.format( param.name(), self.parameterAsInt(parameters, param.name(), context)) else: command += ' -{} {}'.format( param.name(), self.parameterAsDouble(parameters, param.name(), context)) elif isinstance(param, QgsProcessingParameterEnum): command += ' -{} {}'.format( param.name(), self.parameterAsEnum(parameters, param.name(), context)) elif isinstance( param, (QgsProcessingParameterString, QgsProcessingParameterFile)): command += ' -{} "{}"'.format( param.name(), self.parameterAsFile(parameters, param.name(), context)) elif isinstance( param, (QgsProcessingParameterString, QgsProcessingParameterField)): command += ' -{} "{}"'.format( param.name(), self.parameterAsString(parameters, param.name(), context)) output_layers = [] output_files = {} for out in self.destinationParameterDefinitions(): filePath = self.parameterAsOutputLayer(parameters, out.name(), context) if isinstance(out, (QgsProcessingParameterRasterDestination, QgsProcessingParameterVectorDestination)): output_layers.append(filePath) output_files[out.name()] = filePath command += ' -{} "{}"'.format(out.name(), filePath) commands.append(command) # special treatment for RGB algorithm # TODO: improve this and put this code somewhere else for out in self.destinationParameterDefinitions(): if isinstance(out, QgsProcessingParameterRasterDestination): filename = self.parameterAsOutputLayer(parameters, out.name(), context) filename2 = os.path.splitext(filename)[0] + '.sgrd' if self.cmdname == 'RGB Composite': commands.append( 'io_grid_image 0 -IS_RGB -GRID:"{}" -FILE:"{}"'.format( filename2, filename)) # 3: Run SAGA commands = self.editCommands(commands) SagaUtils.createSagaBatchJobFileFromSagaCommands(commands) loglines = [] loglines.append(self.tr('SAGA execution commands')) for line in commands: feedback.pushCommandInfo(line) loglines.append(line) if ProcessingConfig.getSetting(SagaUtils.SAGA_LOG_COMMANDS): QgsMessageLog.logMessage('\n'.join(loglines), self.tr('Processing'), Qgis.Info) SagaUtils.executeSaga(feedback) if crs is not None: for out in output_layers: prjFile = os.path.splitext(out)[0] + '.prj' with open(prjFile, 'w') as f: f.write(crs.toWkt()) result = {} for o in self.outputDefinitions(): if o.name() in output_files: result[o.name()] = output_files[o.name()] return result