40
40
QgsProcessingParameterRasterLayer ,
41
41
QgsProcessingOutputRasterLayer ,
42
42
QgsProcessingParameterString ,
43
- QgsCoordinateTransform )
43
+ QgsCoordinateTransform ,
44
+ QgsMapLayer )
45
+ from qgis .PyQt .QtCore import QObject
44
46
from qgis .analysis import QgsRasterCalculator , QgsRasterCalculatorEntry
45
47
46
48
@@ -77,23 +79,6 @@ def type(self):
77
79
def clone (self ):
78
80
return ParameterRasterCalculatorExpression (self .name (), self .description (), self .multiLine ())
79
81
80
- def evaluateForModeler (self , value , model ):
81
- for i in list (model .inputs .values ()):
82
- param = i .param
83
- if isinstance (param , QgsProcessingParameterRasterLayer ):
84
- new = "{}@" .format (os .path .basename (param .value ))
85
- old = "{}@" .format (param .name ())
86
- value = value .replace (old , new )
87
-
88
- for alg in list (model .algs .values ()):
89
- for out in alg .algorithm .outputs :
90
- if isinstance (out , QgsProcessingOutputRasterLayer ):
91
- if out .value :
92
- new = "{}@" .format (os .path .basename (out .value ))
93
- old = "{}:{}@" .format (alg .modeler_name , out .name )
94
- value = value .replace (old , new )
95
- return value
96
-
97
82
self .addParameter (ParameterRasterCalculatorExpression (self .EXPRESSION , self .tr ('Expression' ),
98
83
multiLine = True ))
99
84
self .addParameter (QgsProcessingParameterMultipleLayers (self .LAYERS ,
@@ -122,17 +107,17 @@ def processAlgorithm(self, parameters, context, feedback):
122
107
123
108
layersDict = {}
124
109
if layers :
125
- layersDict = {os . path . basename ( lyr .source (). split ( "." )[ 0 ] ): lyr for lyr in layers }
110
+ layersDict = {lyr .source (): lyr for lyr in layers }
126
111
127
112
crs = self .parameterAsCrs (parameters , self .CRS , context )
128
- if not layers and not crs .isValid ():
129
- raise QgsProcessingException ( self . tr ( "No reference layer selected nor CRS provided" ))
130
-
131
- if not crs . isValid () and layers :
132
- crs = list (layersDict .values ())[0 ].crs ()
113
+ if crs is None or not crs .isValid ():
114
+ if not layers :
115
+ raise QgsProcessingException ( self . tr ( "No reference layer selected nor CRS provided" ))
116
+ else :
117
+ crs = list (layersDict .values ())[0 ].crs ()
133
118
134
119
bbox = self .parameterAsExtent (parameters , self .EXTENT , context )
135
- if not layers and bbox .isNull ():
120
+ if bbox .isNull () and not layers :
136
121
raise QgsProcessingException (self .tr ("No reference layer selected nor extent box provided" ))
137
122
138
123
if not bbox .isNull ():
@@ -145,7 +130,7 @@ def processAlgorithm(self, parameters, context, feedback):
145
130
bbox = QgsProcessingUtils .combineLayerExtents (layers , crs )
146
131
147
132
cellsize = self .parameterAsDouble (parameters , self .CELLSIZE , context )
148
- if not layers and cellsize == 0 :
133
+ if cellsize == 0 and not layers :
149
134
raise QgsProcessingException (self .tr ("No reference layer selected nor cellsize value provided" ))
150
135
151
136
def _cellsize (layer ):
@@ -157,15 +142,23 @@ def _cellsize(layer):
157
142
if cellsize == 0 :
158
143
cellsize = min ([_cellsize (lyr ) for lyr in layersDict .values ()])
159
144
145
+ # check for layers available in the model
146
+ layersDictCopy = layersDict .copy () # need a shallow copy because next calls invalidate iterator
147
+ for lyr in layersDictCopy .values ():
148
+ expression = self .mappedNameToLayer (lyr , expression , layersDict , context )
149
+
150
+ # check for layers available in the project
160
151
for lyr in QgsProcessingUtils .compatibleRasterLayers (context .project ()):
161
- name = lyr .name ()
162
- if (name + "@" ) in expression :
163
- layersDict [name ] = lyr
152
+ expression = self .mappedNameToLayer (lyr , expression , layersDict , context )
164
153
154
+ # create the list of layers to be passed as inputs to RasterCalculaltor
155
+ # at this phase expression has been modified to match available layers
156
+ # in the current scope
165
157
entries = []
166
158
for name , lyr in layersDict .items ():
167
159
for n in range (lyr .bandCount ()):
168
160
ref = '{:s}@{:d}' .format (name , n + 1 )
161
+
169
162
if ref in expression :
170
163
entry = QgsRasterCalculatorEntry ()
171
164
entry .ref = ref
@@ -178,6 +171,7 @@ def _cellsize(layer):
178
171
width = math .floor ((bbox .xMaximum () - bbox .xMinimum ()) / cellsize )
179
172
height = math .floor ((bbox .yMaximum () - bbox .yMinimum ()) / cellsize )
180
173
driverName = GdalUtils .getFormatShortNameFromFilename (output )
174
+
181
175
calc = QgsRasterCalculator (expression ,
182
176
output ,
183
177
driverName ,
@@ -213,3 +207,80 @@ def processBeforeAddingToModeler(self, algorithm, model):
213
207
values .append (ValueFromOutput (alg .modeler_name , out .name ))
214
208
215
209
algorithm .params [self .LAYERS ] = values
210
+
211
+ def mappedNameToLayer (self , lyr , expression , layersDict , context ):
212
+ '''Try to identify if a real layer is mapped in the expression with a symbolic name.'''
213
+
214
+ nameToMap = lyr .source ()
215
+
216
+ # check if nameToMap is a file
217
+ # TODO: what about URI eg for a COG?
218
+ if os .path .isfile (nameToMap ):
219
+ # get only the name without extension and path of the file
220
+ nameToMap = os .path .splitext (os .path .basename (nameToMap ))[0 ]
221
+
222
+ # check for layers directly added in the expression
223
+ if (nameToMap + "@" ) in expression :
224
+ layersDict [nameToMap ] = lyr
225
+
226
+ # get "algorithm_inputs" scope of the expressionContext related
227
+ # with mapped variables
228
+ indexOfScope = context .expressionContext ().indexOfScope ("algorithm_inputs" )
229
+ if indexOfScope >= 0 :
230
+ expContextAlgInputsScope = context .expressionContext ().scope (indexOfScope )
231
+
232
+ # check for the layers that are mapped as input in a model
233
+ # to do this check in the latest scope all passed variables
234
+ # to look for a variable that is a layer or a string filename ç
235
+ # to a layer
236
+ varDescription = None
237
+ for varName in expContextAlgInputsScope .variableNames ():
238
+
239
+ layerInContext = expContextAlgInputsScope .variable (varName )
240
+
241
+ if not isinstance (layerInContext , str ) and not isinstance (layerInContext , QgsMapLayer ):
242
+ continue
243
+
244
+ if isinstance (layerInContext , QgsMapLayer ) and nameToMap not in layerInContext .source ():
245
+ continue
246
+
247
+ varDescription = expContextAlgInputsScope .description (varName )
248
+
249
+ # because there can be variable with None or "" description
250
+ # then skip them
251
+ if not varDescription :
252
+ continue
253
+
254
+ # check if it's description starts with Output as in:
255
+ # Output 'Output' from algorithm 'calc1'
256
+ # as set in https://github.com/qgis/QGIS/blob/master/src/core/processing/models/qgsprocessingmodelalgorithm.cpp#L516
257
+ # but var in expression is called simply
258
+ # 'Output' from algorithm 'calc1'
259
+
260
+ # get the translatin string to use to parse the description
261
+ # HAVE to use the same translated string as in
262
+ # https://github.com/qgis/QGIS/blob/master/src/core/processing/models/qgsprocessingmodelalgorithm.cpp#L516
263
+ translatedDesc = self .tr ("Output '%1' from algorithm '%2'" )
264
+ elementZero = translatedDesc .split (" " )[0 ] # For english the string result should be "Output"
265
+
266
+ elements = varDescription .split (" " )
267
+ if len (elements ) > 1 and elements [0 ] == elementZero :
268
+ # remove heading QObject.tr"Output ") string. Note adding a space at the end of elementZero!
269
+ varDescription = varDescription [len (elementZero ) + 1 :]
270
+
271
+ # check if cleaned varDescription is present in the expression
272
+ # if not skip it
273
+ if (varDescription + "@" ) not in expression :
274
+ continue
275
+
276
+ # !!!found!!! => substitute in expression
277
+ # and add in the list of layers that will be passed to raster calculator
278
+ nameToMap = varName
279
+ new = "{}@" .format (nameToMap )
280
+ old = "{}@" .format (varDescription )
281
+ expression = expression .replace (old , new )
282
+
283
+ layersDict [nameToMap ] = lyr
284
+
285
+ # need return the modified expression because it's not a reference
286
+ return expression
0 commit comments