QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
qgsrasterpipe.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsrasterpipe.cpp - Internal raster processing modules interface
3  --------------------------------------
4  Date : Jun 21, 2012
5  Copyright : (C) 2012 by Radim Blazek
6  email : radim dot blazek at gmail dot com
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include <typeinfo>
19 
20 #include <QByteArray>
21 
22 #include "qgslogger.h"
23 #include "qgsrasterpipe.h"
24 #include "qgsrasterdataprovider.h"
25 #include "qgsrasterrenderer.h"
28 #include "qgshuesaturationfilter.h"
29 #include "qgsrasterprojector.h"
30 #include "qgsrasternuller.h"
31 
32 #include <mutex>
33 
35 {
36  for ( int i = 0; i < pipe.size(); i++ )
37  {
38  QgsRasterInterface *interface = pipe.at( i );
39  QgsRasterInterface *clone = interface->clone();
40 
41  Qgis::RasterPipeInterfaceRole role = interfaceRole( clone );
42  QgsDebugMsgLevel( QStringLiteral( "cloned interface with role %1" ).arg( qgsEnumValueToKey( role ) ), 4 );
43  if ( i > 0 )
44  {
45  clone->setInput( mInterfaces.at( i - 1 ) );
46  }
47  mInterfaces.append( clone );
48  if ( role != Qgis::RasterPipeInterfaceRole::Unknown )
49  {
50  mRoleMap.insert( role, i );
51  }
52  }
54  mDataDefinedProperties = pipe.mDataDefinedProperties;
55 }
56 
58 {
59  const auto constMInterfaces = mInterfaces;
60  for ( QgsRasterInterface *interface : constMInterfaces )
61  {
62  delete interface;
63  }
64 }
65 
66 bool QgsRasterPipe::connect( QVector<QgsRasterInterface *> interfaces )
67 {
68  QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
69  for ( int i = 1; i < interfaces.size(); i++ )
70  {
71  if ( ! interfaces[i]->setInput( interfaces[i - 1] ) )
72  {
73 #ifdef QGISDEBUG
74  const QgsRasterInterface &a = *interfaces[i];
75  const QgsRasterInterface &b = *interfaces[i - 1];
76  QgsDebugMsg( QStringLiteral( "cannot connect %1 to %2" ).arg( typeid( a ).name(), typeid( b ).name() ) );
77 #endif
78  return false;
79  }
80  }
81  return true;
82 }
83 
84 bool QgsRasterPipe::insert( int idx, QgsRasterInterface *interface )
85 {
86  QgsDebugMsgLevel( QStringLiteral( "insert %1 at %2" ).arg( typeid( *interface ).name() ).arg( idx ), 4 );
87  if ( idx > mInterfaces.size() )
88  {
89  idx = mInterfaces.size();
90  }
91  // make a copy of pipe to test connection, we test the connections
92  // of the whole pipe, because the types and band numbers may change
93  QVector<QgsRasterInterface *> interfaces = mInterfaces;
94 
95  interfaces.insert( idx, interface );
96  bool success = false;
97  if ( connect( interfaces ) )
98  {
99  success = true;
100  mInterfaces.insert( idx, interface );
101  setRole( interface, idx );
102  QgsDebugMsgLevel( QStringLiteral( "Pipe %1 inserted OK" ).arg( idx ), 4 );
103  }
104  else
105  {
106  QgsDebugMsgLevel( QStringLiteral( "Error inserting pipe %1" ).arg( idx ), 4 );
107  }
108 
109  // Connect or reconnect (after the test) interfaces
110  connect( mInterfaces );
111  return success;
112 }
113 
114 bool QgsRasterPipe::replace( int idx, QgsRasterInterface *interface )
115 {
116  if ( !interface ) return false;
117 
118  QgsDebugMsgLevel( QStringLiteral( "replace by %1 at %2" ).arg( typeid( *interface ).name() ).arg( idx ), 4 );
119  if ( !checkBounds( idx ) ) return false;
120 
121  // make a copy of pipe to test connection, we test the connections
122  // of the whole pipe, because the types and band numbers may change
123  QVector<QgsRasterInterface *> interfaces = mInterfaces;
124 
125  interfaces[idx] = interface;
126  bool success = false;
127  if ( connect( interfaces ) )
128  {
129  success = true;
130  delete mInterfaces.at( idx );
131  mInterfaces[idx] = interface;
132  setRole( interface, idx );
133  QgsDebugMsgLevel( QStringLiteral( "replaced OK" ), 4 );
134  }
135 
136  // Connect or reconnect (after the test) interfaces
137  connect( mInterfaces );
138  return success;
139 }
140 
141 Qgis::RasterPipeInterfaceRole QgsRasterPipe::interfaceRole( QgsRasterInterface *interface ) const
142 {
143  Qgis::RasterPipeInterfaceRole role = Qgis::RasterPipeInterfaceRole::Unknown;
144  if ( dynamic_cast<QgsRasterDataProvider *>( interface ) )
145  role = Qgis::RasterPipeInterfaceRole::Provider;
146  else if ( dynamic_cast<QgsRasterRenderer *>( interface ) )
147  role = Qgis::RasterPipeInterfaceRole::Renderer;
148  else if ( dynamic_cast<QgsRasterResampleFilter *>( interface ) )
149  role = Qgis::RasterPipeInterfaceRole::Resampler;
150  else if ( dynamic_cast<QgsBrightnessContrastFilter *>( interface ) )
151  role = Qgis::RasterPipeInterfaceRole::Brightness;
152  else if ( dynamic_cast<QgsHueSaturationFilter *>( interface ) )
153  role = Qgis::RasterPipeInterfaceRole::HueSaturation;
154  else if ( dynamic_cast<QgsRasterProjector *>( interface ) )
155  role = Qgis::RasterPipeInterfaceRole::Projector;
156  else if ( dynamic_cast<QgsRasterNuller *>( interface ) )
157  role = Qgis::RasterPipeInterfaceRole::Nuller;
158 
159  QgsDebugMsgLevel( QStringLiteral( "%1 role = %2" ).arg( typeid( *interface ).name(), qgsEnumValueToKey( role ) ), 4 );
160  return role;
161 }
162 
163 void QgsRasterPipe::setRole( QgsRasterInterface *interface, int idx )
164 {
165  Qgis::RasterPipeInterfaceRole role = interfaceRole( interface );
166  if ( role == Qgis::RasterPipeInterfaceRole::Unknown )
167  return;
168 
169  mRoleMap.insert( role, idx );
170 }
171 
172 void QgsRasterPipe::unsetRole( QgsRasterInterface *interface )
173 {
174  Qgis::RasterPipeInterfaceRole role = interfaceRole( interface );
175  if ( role == Qgis::RasterPipeInterfaceRole::Unknown )
176  return;
177 
178  const int roleIdx{ mRoleMap[role] };
179  mRoleMap.remove( role );
180 
181  // Decrease all indexes greater than the removed one
182  const auto roleMapValues {mRoleMap.values()};
183  if ( roleIdx < *std::max_element( roleMapValues.begin(), roleMapValues.end() ) )
184  {
185  for ( auto it = mRoleMap.cbegin(); it != mRoleMap.cend(); ++it )
186  {
187  if ( it.value() > roleIdx )
188  {
189  mRoleMap[it.key()] = it.value() - 1;
190  }
191  }
192  }
193 }
194 
196 {
197  if ( !interface )
198  return false;
199 
200  QgsDebugMsgLevel( typeid( *interface ).name(), 4 );
201  Qgis::RasterPipeInterfaceRole role = interfaceRole( interface );
202 
203  // We don't know where to place unknown interface
204  if ( role == Qgis::RasterPipeInterfaceRole::Unknown )
205  return false;
206 
207  //if ( mInterfacesMap.value ( role ) )
208  if ( mRoleMap.contains( role ) )
209  {
210  // An old interface of the same role exists -> replace
211  // replace may still fail and return false
212  return replace( mRoleMap.value( role ), interface );
213  }
214 
215  int idx = 0;
216 
217  // Not found, find the best default position for this kind of interface
218  // QgsRasterDataProvider - ProviderRole
219  // QgsRasterRenderer - RendererRole
220  // QgsRasterResampler - ResamplerRole
221  // QgsRasterProjector - ProjectorRole
222 
223  int providerIdx = mRoleMap.value( Qgis::RasterPipeInterfaceRole::Provider, -1 );
224  int rendererIdx = mRoleMap.value( Qgis::RasterPipeInterfaceRole::Renderer, -1 );
225  int resamplerIdx = mRoleMap.value( Qgis::RasterPipeInterfaceRole::Resampler, -1 );
226  int brightnessIdx = mRoleMap.value( Qgis::RasterPipeInterfaceRole::Brightness, -1 );
227  int hueSaturationIdx = mRoleMap.value( Qgis::RasterPipeInterfaceRole::HueSaturation, -1 );
228 
229  if ( role == Qgis::RasterPipeInterfaceRole::Provider )
230  {
231  idx = 0;
232  }
233  else if ( role == Qgis::RasterPipeInterfaceRole::Renderer )
234  {
235  idx = providerIdx + 1;
236  }
237  else if ( role == Qgis::RasterPipeInterfaceRole::Brightness )
238  {
239  idx = std::max( providerIdx, rendererIdx ) + 1;
240  }
241  else if ( role == Qgis::RasterPipeInterfaceRole::HueSaturation )
242  {
243  idx = std::max( std::max( providerIdx, rendererIdx ), brightnessIdx ) + 1;
244  }
245  else if ( role == Qgis::RasterPipeInterfaceRole::Resampler )
246  {
247  idx = std::max( std::max( std::max( providerIdx, rendererIdx ), brightnessIdx ), hueSaturationIdx ) + 1;
248  }
249  else if ( role == Qgis::RasterPipeInterfaceRole::Projector )
250  {
251  idx = std::max( std::max( std::max( std::max( providerIdx, rendererIdx ), brightnessIdx ), hueSaturationIdx ), resamplerIdx ) + 1;
252  }
253 
254  return insert( idx, interface ); // insert may still fail and return false
255 }
256 
257 QgsRasterInterface *QgsRasterPipe::interface( Qgis::RasterPipeInterfaceRole role ) const
258 {
259  QgsDebugMsgLevel( QStringLiteral( "role = %1" ).arg( qgsEnumValueToKey( role ) ), 4 );
260  if ( mRoleMap.contains( role ) )
261  {
262  return mInterfaces.value( mRoleMap.value( role ) );
263  }
264  return nullptr;
265 }
266 
268 {
269  return dynamic_cast<QgsRasterDataProvider *>( interface( Qgis::RasterPipeInterfaceRole::Provider ) );
270 }
271 
273 {
274  return dynamic_cast<QgsRasterRenderer *>( interface( Qgis::RasterPipeInterfaceRole::Renderer ) );
275 }
276 
278 {
279  return dynamic_cast<QgsRasterResampleFilter *>( interface( Qgis::RasterPipeInterfaceRole::Resampler ) );
280 }
281 
283 {
284  return dynamic_cast<QgsBrightnessContrastFilter *>( interface( Qgis::RasterPipeInterfaceRole::Brightness ) );
285 }
286 
288 {
289  return dynamic_cast<QgsHueSaturationFilter *>( interface( Qgis::RasterPipeInterfaceRole::HueSaturation ) );
290 }
291 
293 {
294  return dynamic_cast<QgsRasterProjector *>( interface( Qgis::RasterPipeInterfaceRole::Projector ) );
295 }
296 
298 {
299  return dynamic_cast<QgsRasterNuller *>( interface( Qgis::RasterPipeInterfaceRole::Nuller ) );
300 }
301 
302 bool QgsRasterPipe::remove( int idx )
303 {
304  QgsDebugMsgLevel( QStringLiteral( "remove at %1" ).arg( idx ), 4 );
305 
306  if ( !checkBounds( idx ) )
307  return false;
308 
309  // make a copy of pipe to test connection, we test the connections
310  // of the whole pipe, because the types and band numbers may change
311  QVector<QgsRasterInterface *> interfaces = mInterfaces;
312 
313  interfaces.remove( idx );
314  bool success = false;
315  if ( connect( interfaces ) )
316  {
317  success = true;
318  unsetRole( mInterfaces.at( idx ) );
319  delete mInterfaces.at( idx );
320  mInterfaces.remove( idx );
321  QgsDebugMsgLevel( QStringLiteral( "Pipe %1 removed OK" ).arg( idx ), 4 );
322  }
323  else
324  {
325  QgsDebugMsgLevel( QStringLiteral( "Error removing pipe %1" ).arg( idx ), 4 );
326  }
327 
328  // Connect or reconnect (after the test) interfaces
329  connect( mInterfaces );
330 
331  return success;
332 }
333 
335 {
336  if ( !interface ) return false;
337 
338  return remove( mInterfaces.indexOf( interface ) );
339 }
340 
341 bool QgsRasterPipe::canSetOn( int idx, bool on )
342 {
343  QgsDebugMsgLevel( QStringLiteral( "idx = %1 on = %2" ).arg( idx ).arg( on ), 4 );
344  if ( !checkBounds( idx ) )
345  return false;
346 
347  // Because setting interface on/off may change its output we must check if
348  // connection is OK after such switch
349  bool onOrig = mInterfaces.at( idx )->on();
350 
351  if ( onOrig == on )
352  return true;
353 
354  mInterfaces.at( idx )->setOn( on );
355 
356  bool success = connect( mInterfaces );
357 
358  mInterfaces.at( idx )->setOn( onOrig );
359  connect( mInterfaces );
360  return success;
361 }
362 
363 bool QgsRasterPipe::setOn( int idx, bool on )
364 {
365  QgsDebugMsgLevel( QStringLiteral( "idx = %1 on = %2" ).arg( idx ).arg( on ), 4 );
366  if ( !checkBounds( idx ) )
367  return false;
368 
369  bool onOrig = mInterfaces.at( idx )->on();
370 
371  if ( onOrig == on )
372  return true;
373 
374  mInterfaces.at( idx )->setOn( on );
375 
376  if ( connect( mInterfaces ) )
377  return true;
378 
379  mInterfaces.at( idx )->setOn( onOrig );
380  connect( mInterfaces );
381 
382  return false;
383 }
384 
385 bool QgsRasterPipe::checkBounds( int idx ) const
386 {
387  return !( idx < 0 || idx >= mInterfaces.size() );
388 }
389 
391 {
392  mResamplingStage = stage;
393 
394  int resamplerIndex = 0;
395  for ( QgsRasterInterface *interface : std::as_const( mInterfaces ) )
396  {
397  if ( interfaceRole( interface ) == Qgis::RasterPipeInterfaceRole::Resampler )
398  {
399  setOn( resamplerIndex, stage == Qgis::RasterResamplingStage::ResampleFilter );
400  break;
401  }
402  resamplerIndex ++;
403  }
404 
405  if ( QgsRasterDataProvider *l_provider = provider() )
406  {
407  l_provider->enableProviderResampling( stage == Qgis::RasterResamplingStage::Provider );
408  }
409 }
410 
412 {
413  if ( !mDataDefinedProperties.hasActiveProperties() )
414  return;
415 
416  if ( mDataDefinedProperties.isActive( RendererOpacity ) )
417  {
418  if ( QgsRasterRenderer *r = renderer() )
419  {
420  const double prevOpacity = r->opacity();
421  context.setOriginalValueVariable( prevOpacity * 100 );
422  bool ok = false;
423  const double opacity = mDataDefinedProperties.valueAsDouble( RendererOpacity, context, prevOpacity, &ok ) / 100;
424  if ( ok )
425  {
426  r->setOpacity( opacity );
427  }
428  }
429  }
430 }
431 
432 QgsPropertiesDefinition QgsRasterPipe::sPropertyDefinitions;
433 
434 void QgsRasterPipe::initPropertyDefinitions()
435 {
436  const QString origin = QStringLiteral( "raster" );
437 
438  sPropertyDefinitions = QgsPropertiesDefinition
439  {
440  { QgsRasterPipe::RendererOpacity, QgsPropertyDefinition( "RendererOpacity", QObject::tr( "Renderer opacity" ), QgsPropertyDefinition::Opacity, origin ) },
441  };
442 }
443 
445 {
446  static std::once_flag initialized;
447  std::call_once( initialized, [ = ]( )
448  {
449  initPropertyDefinitions();
450  } );
451  return sPropertyDefinitions;
452 }
RasterResamplingStage
Stage at which raster resampling occurs.
Definition: qgis.h:488
@ Provider
Resampling occurs in Provider.
@ ResampleFilter
Resampling occurs in ResamplingFilter.
RasterPipeInterfaceRole
Sublayer flags.
Definition: qgis.h:471
double valueAsDouble(int key, const QgsExpressionContext &context, double defaultValue=0.0, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a double.
Brightness/contrast and gamma correction filter pipe for rasters.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for the context.
Color and saturation filter pipe for rasters.
bool isActive(int key) const override
Returns true if the collection contains an active property with the specified key.
bool hasActiveProperties() const override
Returns true if the collection has any active properties, or false if all properties within the colle...
Definition for a property.
Definition: qgsproperty.h:48
@ Opacity
Opacity (0-100)
Definition: qgsproperty.h:63
Base class for raster data providers.
Base class for processing filters like renderers, reprojector, resampler etc.
virtual QgsRasterInterface * clone() const =0
Clone itself, create deep copy.
Raster pipe that deals with null values.
Contains a pipeline of raster interfaces for sequential raster processing.
Definition: qgsrasterpipe.h:50
bool set(QgsRasterInterface *interface)
Inserts a new known interface in default place or replace interface of the same role if it already ex...
int size() const
Returns the size of the pipe (the number of interfaces contained in the pipe).
QgsRasterPipe()=default
Constructor for an empty QgsRasterPipe.
QgsRasterResampleFilter * resampleFilter() const
Returns the resample filter interface, or nullptr if no resample filter is present in the pipe.
bool replace(int idx, QgsRasterInterface *interface)
Attempts to replace the interface at specified index and reconnect the pipe.
QgsRasterDataProvider * provider() const
Returns the data provider interface, or nullptr if no data provider is present in the pipe.
bool canSetOn(int idx, bool on)
Returns true if the interface at the specified index may be switched on or off.
bool insert(int idx, QgsRasterInterface *interface)
Attempts to insert interface at specified index and connect if connection would fail,...
void setResamplingStage(Qgis::RasterResamplingStage stage)
Sets which stage of the pipe should apply resampling.
void evaluateDataDefinedProperties(QgsExpressionContext &context)
Evaluates any data defined properties set on the pipe, applying their results to the corresponding in...
QgsRasterProjector * projector() const
Returns the projector interface, or nullptr if no projector is present in the pipe.
bool remove(int idx)
Removes and deletes the interface at given index (if possible).
static QgsPropertiesDefinition propertyDefinitions()
Returns the definitions for data defined properties available for use in raster pipes.
QgsRasterRenderer * renderer() const
Returns the raster renderer interface, or nullptr if no raster renderer is present in the pipe.
@ RendererOpacity
Raster renderer global opacity.
Definition: qgsrasterpipe.h:59
Qgis::RasterResamplingStage resamplingStage() const
Returns which stage of the pipe should apply resampling.
QgsBrightnessContrastFilter * brightnessFilter() const
Returns the brightness filter interface, or nullptr if no brightness filter is present in the pipe.
QgsHueSaturationFilter * hueSaturationFilter() const
Returns the hue/saturation interface, or nullptr if no hue/saturation filter is present in the pipe.
QgsRasterNuller * nuller() const
Returns the raster nuller interface, or nullptr if no raster nuller is present in the pipe.
bool setOn(int idx, bool on)
Set whether the interface at the specified index is enabled.
QgsRasterProjector implements approximate projection support for it calculates grid of points in sour...
Raster renderer pipe that applies colors to a raster.
Resample filter pipe for rasters.
QString qgsEnumValueToKey(const T &value)
Returns the value for the given key of an enum.
Definition: qgis.h:1405
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QMap< int, QgsPropertyDefinition > QgsPropertiesDefinition
Definition of available properties.