mirror of
				https://github.com/qgis/QGIS.git
				synced 2025-10-31 00:06:02 -04:00 
			
		
		
		
	Prevent changes to files that weren't changed between releases. This eases review of the changes between releases significantly.
		
			
				
	
	
		
			280 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			280 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # -*- coding: utf-8 -*-
 | |
| """
 | |
| ***************************************************************************
 | |
|     OtbAlgorithm.py
 | |
|     ---------------
 | |
|     Date                 : June 2017
 | |
|     Copyright            : (C) 2017 by CS Systemes d'Information (CS SI)
 | |
|                          : (C) 2018 by Centre National d'Etudes et spatiales (CNES)
 | |
|     Email                : rashad dot kanavath at c-s fr, otb at c-s dot fr (CS SI)
 | |
| 
 | |
| ***************************************************************************
 | |
| *                                                                         *
 | |
| *   This program is free software; you can redistribute it and/or modify  *
 | |
| *   it under the terms of the GNU General Public License as published by  *
 | |
| *   the Free Software Foundation; either version 2 of the License, or     *
 | |
| *   (at your option) any later version.                                   *
 | |
| *                                                                         *
 | |
| ***************************************************************************
 | |
| """
 | |
| 
 | |
| __author__ = 'Rashad Kanavath'
 | |
| __date__ = 'June 2017'
 | |
| __copyright__ = "(C) 2017,2018 by CS Systemes d'information (CS SI), Centre National d'Etudes et spatiales (CNES)"
 | |
| 
 | |
| import os
 | |
| 
 | |
| from qgis.PyQt.QtCore import QCoreApplication
 | |
| from qgis.PyQt.QtGui import QIcon
 | |
| 
 | |
| from qgis.core import (Qgis,
 | |
|                        QgsMessageLog,
 | |
|                        QgsMapLayer,
 | |
|                        QgsApplication,
 | |
|                        QgsProcessingException,
 | |
|                        QgsProcessingAlgorithm,
 | |
|                        QgsProcessingParameterMultipleLayers,
 | |
|                        QgsProcessingParameterDefinition,
 | |
|                        QgsProcessingOutputLayerDefinition,
 | |
|                        QgsProcessingParameterCrs,
 | |
|                        QgsProcessingParameterString,
 | |
|                        QgsProcessingParameterRasterLayer,
 | |
|                        QgsProcessingParameterVectorLayer,
 | |
|                        QgsProcessingParameterBoolean,
 | |
|                        QgsProcessingParameterFile,
 | |
|                        QgsProcessingParameterNumber,
 | |
|                        QgsProcessingParameterRasterDestination,
 | |
|                        QgsProcessingParameterVectorDestination,
 | |
|                        QgsProcessingParameterEnum)
 | |
| 
 | |
| from processing.core.parameters import getParameterFromString
 | |
| from processing.algs.otb.OtbChoiceWidget import OtbParameterChoice
 | |
| from processing.algs.otb.OtbUtils import OtbUtils
 | |
| 
 | |
| 
 | |
| class OtbAlgorithm(QgsProcessingAlgorithm):
 | |
| 
 | |
|     def __init__(self, group, name, descriptionfile, display_name='', groupId=''):
 | |
|         super().__init__()
 | |
|         self._name = name
 | |
|         self._group = group
 | |
|         self._display_name = display_name
 | |
| 
 | |
|         self._groupId = ''
 | |
|         validChars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789:'
 | |
|         if not groupId:
 | |
|             self._groupId = ''.join(c for c in self._group if c in validChars)
 | |
| 
 | |
|         self.pixelTypes = ['uint8', 'uint16', 'int16', 'uint32', 'int32',
 | |
|                            'float', 'double', 'cint16', 'cint32', 'cfloat', 'cdouble']
 | |
|         self._descriptionfile = descriptionfile
 | |
|         self.defineCharacteristicsFromFile()
 | |
| 
 | |
|     def icon(self):
 | |
|         return QgsApplication.getThemeIcon("/providerOtb.svg")
 | |
| 
 | |
|     def createInstance(self):
 | |
|         return self.__class__(self._group, self._name, self._descriptionfile)
 | |
| 
 | |
|     def tr(self, string):
 | |
|         return QCoreApplication.translate("OtbAlgorithm", string)
 | |
| 
 | |
|     def name(self):
 | |
|         return self._name
 | |
| 
 | |
|     def displayName(self):
 | |
|         return self._display_name
 | |
| 
 | |
|     def group(self):
 | |
|         return self._group
 | |
| 
 | |
|     def groupId(self):
 | |
|         return self._groupId
 | |
| 
 | |
|     def descriptionfile(self):
 | |
|         return self._descriptionfile
 | |
| 
 | |
|     def initAlgorithm(self, config=None):
 | |
|         pass
 | |
| 
 | |
|     #TODO: show version which is same as OtbAlgorithm rather than always latest.
 | |
|     def helpUrl(self):
 | |
|         return "https://www.orfeo-toolbox.org/CookBook/Applications/app_" + self.name() + ".html"
 | |
| 
 | |
|     def defineCharacteristicsFromFile(self):
 | |
|         line = None
 | |
|         try:
 | |
|             with open(self._descriptionfile) as lines:
 | |
|                 line = lines.readline().strip('\n').strip()
 | |
|                 self._name = line.split('|')[0]
 | |
|                 self.appkey = self._name
 | |
|                 line = lines.readline().strip('\n').strip()
 | |
|                 self.doc = line
 | |
|                 self.i18n_doc = QCoreApplication.translate("OtbAlgorithm", self.doc)
 | |
|                 #self._name = self._name #+ " - " + self.doc
 | |
|                 self._display_name = self.tr(self._name)
 | |
|                 self.i18n_name = QCoreApplication.translate("OtbAlgorithm", self._name)
 | |
| 
 | |
|                 line = lines.readline().strip('\n').strip()
 | |
|                 self._group = line
 | |
|                 self.i18n_group = QCoreApplication.translate("OtbAlgorithm", self._group)
 | |
|                 line = lines.readline().strip('\n').strip()
 | |
|                 while line != '':
 | |
|                     line = line.strip('\n').strip()
 | |
|                     if line.startswith('#'):
 | |
|                         line = lines.readline().strip('\n').strip()
 | |
|                         continue
 | |
|                     param = None
 | |
|                     if 'OTBParameterChoice' in line:
 | |
|                         tokens = line.split("|")
 | |
|                         params = [t if str(t) != str(None) else None for t in tokens[1:]]
 | |
|                         options = params[2].split(';')
 | |
|                         param = OtbParameterChoice(params[0], params[1], options, params[3], params[4])
 | |
|                     else:
 | |
|                         param = getParameterFromString(line, 'OtbAlgorithm')
 | |
| 
 | |
|                     #if parameter is None, then move to next line and continue
 | |
|                     if param is None:
 | |
|                         line = lines.readline().strip('\n').strip()
 | |
|                         continue
 | |
| 
 | |
|                     name = param.name()
 | |
|                     if '.' in name and len(name.split('.')) > 2:
 | |
|                         p = name.split('.')[:-2]
 | |
|                         group_key = '.'.join(p)
 | |
|                         group_value = name.split('.')[-2]
 | |
|                         metadata = param.metadata()
 | |
|                         metadata['group_key'] = group_key
 | |
|                         metadata['group_value'] = group_value
 | |
|                         param.setMetadata(metadata)
 | |
| 
 | |
|                     #'elev.dem.path', 'elev.dem', 'elev.dem.geoid', 'elev.geoid' are special!
 | |
|                     #Even though it is not typical for OTB to fix on parameter keys,
 | |
|                     #we current use below !hack! to set SRTM path and GEOID files
 | |
|                     #Future releases of OTB must follow this rule keep
 | |
|                     #compatibility or update this checklist.
 | |
|                     if name in ["elev.dem.path", "elev.dem"]:
 | |
|                         param.setDefaultValue(OtbUtils.srtmFolder())
 | |
|                     if name in ["elev.dem.geoid", "elev.geoid"]:
 | |
|                         param.setDefaultValue(OtbUtils.geoidFile())
 | |
| 
 | |
|                     # outputpixeltype is a special parameter associated with raster output
 | |
|                     # reset list of options to 'self.pixelTypes'.
 | |
|                     if name == 'outputpixeltype':
 | |
|                         param.setOptions(self.pixelTypes)
 | |
|                         param.setDefaultValue(self.pixelTypes.index('float'))
 | |
| 
 | |
|                     self.addParameter(param)
 | |
|                     #parameter is added now and we must move to next line
 | |
|                     line = lines.readline().strip('\n').strip()
 | |
| 
 | |
|         except BaseException as e:
 | |
|             import traceback
 | |
|             errmsg = "Could not open OTB algorithm from file: \n" + self._descriptionfile + "\nline=" + line + "\nError:\n" + traceback.format_exc()
 | |
|             QgsMessageLog.logMessage(self.tr(errmsg), self.tr('Processing'), Qgis.Critical)
 | |
|             raise e
 | |
| 
 | |
|     def preprocessParameters(self, parameters):
 | |
|         valid_params = {}
 | |
|         for k, v in parameters.items():
 | |
|             param = self.parameterDefinition(k)
 | |
|             #If parameterDefinition(k) return None, this is considered a invalid parameter.
 | |
|             #just continue for loop
 | |
|             if param is None:
 | |
|                 continue
 | |
| 
 | |
|             # Any other valid parameters have:
 | |
|             #- empty or no metadata
 | |
|             #- metadata without a 'group_key'
 | |
|             #- metadata with 'group_key' and 'group_key' is not in list of parameters. see ParameterGroup in OTB
 | |
|             #- metadata with 'group_key' and 'group_key' is a valid parameter and it's value == metadata()['group_value']
 | |
|             if 'group_key' in param.metadata() and not param.metadata()['group_key'] in parameters:
 | |
|                 valid_params[k] = v
 | |
|             elif not 'group_key' in param.metadata() or parameters[param.metadata()['group_key']] == param.metadata()['group_value']:
 | |
|                 valid_params[k] = v
 | |
| 
 | |
|         return valid_params
 | |
| 
 | |
|     def processAlgorithm(self, parameters, context, feedback):
 | |
|         app_launcher_path = OtbUtils.getExecutableInPath(OtbUtils.otbFolder(), 'otbApplicationLauncherCommandLine')
 | |
|         command = '"{}" {} {}'.format(app_launcher_path, self.name(), OtbUtils.appFolder())
 | |
|         outputPixelType = None
 | |
|         for k, v in parameters.items():
 | |
|             # if value is None for a parameter we don't have any businees with this key
 | |
|             if not v or v is None:
 | |
|                 continue
 | |
|             # for 'outputpixeltype' parameter we find the pixeltype string from self.pixelTypes
 | |
|             if k == 'outputpixeltype':
 | |
|                 pixel_type = self.pixelTypes[int(parameters['outputpixeltype'])]
 | |
|                 outputPixelType = None if pixel_type == 'float' else pixel_type
 | |
|                 continue
 | |
| 
 | |
|             param = self.parameterDefinition(k)
 | |
|             if param.isDestination():
 | |
|                 continue
 | |
|             if isinstance(param, QgsProcessingParameterEnum):
 | |
|                 value = self.parameterAsEnum(parameters, param.name(), context)
 | |
|             elif isinstance(param, QgsProcessingParameterBoolean):
 | |
|                 value = self.parameterAsBoolean(parameters, param.name(), context)
 | |
|             elif isinstance(param, QgsProcessingParameterCrs):
 | |
|                 crsValue = self.parameterAsCrs(parameters, param.name(), context)
 | |
|                 authid = crsValue.authid()
 | |
|                 if authid.startswith('EPSG:'):
 | |
|                     value = authid.split('EPSG:')[1]
 | |
|                 else:
 | |
|                     raise QgsProcessingException(
 | |
|                         self.tr("Incorrect value for parameter '{}'. No EPSG code found in '{}'".format(
 | |
|                             param.name(),
 | |
|                             authid)))
 | |
|             elif isinstance(param, QgsProcessingParameterFile):
 | |
|                 value = self.parameterAsFile(parameters, param.name(), context)
 | |
|             elif isinstance(param, QgsProcessingParameterMultipleLayers):
 | |
|                 layers = self.parameterAsLayerList(parameters, param.name(), context)
 | |
|                 if layers is None or len(layers) == 0:
 | |
|                     continue
 | |
|                 value = ' '.join(['"{}"'.format(self.getLayerSource(param.name(), layer)) for layer in layers])
 | |
|             elif isinstance(param, QgsProcessingParameterNumber):
 | |
|                 if param.dataType() == QgsProcessingParameterNumber.Integer:
 | |
|                     value = self.parameterAsInt(parameters, param.name(), context)
 | |
|                 else:
 | |
|                     value = self.parameterAsDouble(parameters, param.name(), context)
 | |
|             elif isinstance(param, (QgsProcessingParameterRasterLayer, QgsProcessingParameterVectorLayer)):
 | |
|                 value = '"{}"'.format(self.getLayerSource(param.name(), self.parameterAsLayer(parameters, param.name(), context)))
 | |
|             elif isinstance(param, QgsProcessingParameterString):
 | |
|                 value = '"{}"'.format(self.parameterAsString(parameters, param.name(), context))
 | |
|             else:
 | |
|                 # Use whatever is given
 | |
|                 value = '"{}"'.format(parameters[param.name()])
 | |
| 
 | |
|             # Check if value is set in above if elif ladder and update command string
 | |
|             if value and value is not None:
 | |
|                 command += ' -{} {}'.format(k, value)
 | |
| 
 | |
|         output_files = {}
 | |
| 
 | |
|         for out in self.destinationParameterDefinitions():
 | |
|             filePath = self.parameterAsOutputLayer(parameters, out.name(), context)
 | |
|             if filePath:
 | |
|                 output_files[out.name()] = filePath
 | |
|                 if outputPixelType is not None:
 | |
|                     command += ' -{} "{}" "{}"'.format(out.name(), filePath, outputPixelType)
 | |
|                 else:
 | |
|                     command += ' -{} "{}"'.format(out.name(), filePath)
 | |
| 
 | |
|         OtbUtils.executeOtb(command, feedback)
 | |
| 
 | |
|         result = {}
 | |
|         for o in self.outputDefinitions():
 | |
|             if o.name() in output_files:
 | |
|                 result[o.name()] = output_files[o.name()]
 | |
|         return result
 | |
| 
 | |
|     def getLayerSource(self, name, layer):
 | |
|         providerName = layer.dataProvider().name()
 | |
|         #TODO: add other provider support in OTB, eg: memory
 | |
|         if providerName in ['ogr', 'gdal']:
 | |
|             return layer.source()
 | |
|         else:
 | |
|             raise QgsProcessingException(
 | |
|                 self.tr("OTB currently support only gdal and ogr provider. Parameter '{}' uses '{}' provider".format(name, providerName)))
 |