QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
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"
25#include "qgsrasterrenderer.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
66bool 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
84bool 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
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
141Qgis::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
163void 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
172void 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
257QgsRasterInterface *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
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
341bool 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
363bool 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
385bool 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
432QgsPropertiesDefinition QgsRasterPipe::sPropertyDefinitions;
433
434void 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:721
@ Provider
Resampling occurs in Provider.
@ ResampleFilter
Resampling occurs in ResamplingFilter.
RasterPipeInterfaceRole
Sublayer flags.
Definition: qgis.h:704
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:46
@ Opacity
Opacity (0-100)
Definition: qgsproperty.h:61
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, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition: qgis.h:2681
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QMap< int, QgsPropertyDefinition > QgsPropertiesDefinition
Definition of available properties.