QGIS API Documentation 3.99.0-Master (2fe06baccd8)
Loading...
Searching...
No Matches
qgshillshaderenderer.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgshillshaderenderer.cpp
3 ---------------------------------
4 begin : May 2016
5 copyright : (C) 2016 by Nathan Woodrow
6 email : woodrow dot nathan 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
19
20#include <memory>
21
22#include "qgsmessagelog.h"
23#include "qgsrasterblock.h"
24#include "qgsrasterinterface.h"
26#include "qgsrectangle.h"
27#include "qgssldexportcontext.h"
28
29#include <QColor>
30
31#ifdef HAVE_OPENCL
32#ifdef QGISDEBUG
33#include <chrono>
34#include "qgssettings.h"
35#endif
36#include "qgsexception.h"
37#include "qgsopenclutils.h"
38#endif
39
40QgsHillshadeRenderer::QgsHillshadeRenderer( QgsRasterInterface *input, int band, double lightAzimuth, double lightAngle ):
41 QgsRasterRenderer( input, QStringLiteral( "hillshade" ) )
42 , mBand( band )
43 , mLightAngle( lightAngle )
44 , mLightAzimuth( lightAzimuth )
45{
46
47}
48
50{
51 QgsHillshadeRenderer *r = new QgsHillshadeRenderer( nullptr, mBand, mLightAzimuth, mLightAngle );
52 r->copyCommonProperties( this );
53
54 r->setZFactor( mZFactor );
55 r->setMultiDirectional( mMultiDirectional );
56 return r;
57}
58
63
65{
66 if ( elem.isNull() )
67 {
68 return nullptr;
69 }
70
71 int band = elem.attribute( QStringLiteral( "band" ), QStringLiteral( "0" ) ).toInt();
72 double azimuth = elem.attribute( QStringLiteral( "azimuth" ), QStringLiteral( "315" ) ).toDouble();
73 double angle = elem.attribute( QStringLiteral( "angle" ), QStringLiteral( "45" ) ).toDouble();
74 double zFactor = elem.attribute( QStringLiteral( "zfactor" ), QStringLiteral( "1" ) ).toDouble();
75 bool multiDirectional = elem.attribute( QStringLiteral( "multidirection" ), QStringLiteral( "0" ) ).toInt();
77 r->readXml( elem );
78
79 r->setZFactor( zFactor );
81 return r;
82}
83
84void QgsHillshadeRenderer::writeXml( QDomDocument &doc, QDomElement &parentElem ) const
85{
86 if ( parentElem.isNull() )
87 {
88 return;
89 }
90
91 QDomElement rasterRendererElem = doc.createElement( QStringLiteral( "rasterrenderer" ) );
92 _writeXml( doc, rasterRendererElem );
93
94 rasterRendererElem.setAttribute( QStringLiteral( "band" ), mBand );
95 rasterRendererElem.setAttribute( QStringLiteral( "azimuth" ), QString::number( mLightAzimuth ) );
96 rasterRendererElem.setAttribute( QStringLiteral( "angle" ), QString::number( mLightAngle ) );
97 rasterRendererElem.setAttribute( QStringLiteral( "zfactor" ), QString::number( mZFactor ) );
98 rasterRendererElem.setAttribute( QStringLiteral( "multidirection" ), QString::number( mMultiDirectional ) );
99 parentElem.appendChild( rasterRendererElem );
100}
101
102QgsRasterBlock *QgsHillshadeRenderer::block( int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback *feedback )
103{
104 Q_UNUSED( bandNo )
105 auto outputBlock = std::make_unique<QgsRasterBlock>();
106 if ( !mInput )
107 {
108 QgsDebugError( QStringLiteral( "No input raster!" ) );
109 return outputBlock.release();
110 }
111
112 std::shared_ptr< QgsRasterBlock > inputBlock( mInput->block( mBand, extent, width, height, feedback ) );
113
114 if ( !inputBlock || inputBlock->isEmpty() )
115 {
116 QgsDebugError( QStringLiteral( "No raster data!" ) );
117 return outputBlock.release();
118 }
119
120 std::shared_ptr< QgsRasterBlock > alphaBlock;
121
122 if ( mAlphaBand > 0 && mBand != mAlphaBand )
123 {
124 alphaBlock.reset( mInput->block( mAlphaBand, extent, width, height, feedback ) );
125 if ( !alphaBlock || alphaBlock->isEmpty() )
126 {
127 // TODO: better to render without alpha
128 return outputBlock.release();
129 }
130 }
131 else if ( mAlphaBand > 0 )
132 {
133 alphaBlock = inputBlock;
134 }
135
136 if ( !outputBlock->reset( Qgis::DataType::ARGB32_Premultiplied, width, height ) )
137 {
138 return outputBlock.release();
139 }
140
141 if ( width == 0 || height == 0 )
142 return outputBlock.release();
143
144 // Starting the computation
145
146 // Common pre-calculated values
147 float cellXSize = static_cast<float>( extent.width() ) / width;
148 float cellYSize = static_cast<float>( extent.height() ) / height;
149 float zenithRad = static_cast<float>( std::max( 0.0, 90 - mLightAngle ) * M_PI / 180.0 );
150 float azimuthRad = static_cast<float>( -1 * mLightAzimuth * M_PI / 180.0 );
151 float cosZenithRad = std::cos( zenithRad );
152 float sinZenithRad = std::sin( zenithRad );
153
154 // For fast formula from GDAL DEM
155 float cos_alt_mul_z = cosZenithRad * static_cast<float>( mZFactor );
156 float cos_az_mul_cos_alt_mul_z = std::cos( azimuthRad ) * cos_alt_mul_z;
157 float sin_az_mul_cos_alt_mul_z = std::sin( azimuthRad ) * cos_alt_mul_z;
158 float cos_az_mul_cos_alt_mul_z_mul_254 = 254.0f * cos_az_mul_cos_alt_mul_z;
159 float sin_az_mul_cos_alt_mul_z_mul_254 = 254.0f * sin_az_mul_cos_alt_mul_z;
160 float square_z = static_cast<float>( mZFactor * mZFactor );
161 float sin_altRadians_mul_254 = 254.0f * sinZenithRad;
162
163 // For multi directional
164 float sin_altRadians_mul_127 = 127.0f * sinZenithRad;
165 // 127.0 * std::cos(225.0 * M_PI / 180.0) = -32.87001872802012
166 float cos225_az_mul_cos_alt_mul_z_mul_127 = -32.87001872802012f * cos_alt_mul_z;
167 float cos_alt_mul_z_mul_127 = 127.0f * cos_alt_mul_z;
168
169 const QRgb defaultNodataColor = renderColorForNodataPixel();
170
171#ifdef HAVE_OPENCL
172
173 // Use OpenCL? For now OpenCL is enabled in the default configuration only
174 bool useOpenCL( QgsOpenClUtils::enabled()
176 && ( ! mRasterTransparency || mRasterTransparency->isEmpty() )
177 && mAlphaBand <= 0
178 && inputBlock->dataTypeSize() <= 4 );
179 // Check for sources
180 QString source;
181 if ( useOpenCL )
182 {
183 source = QgsOpenClUtils::sourceFromBaseName( QStringLiteral( "hillshade_renderer" ) );
184 if ( source.isEmpty() )
185 {
186 useOpenCL = false;
187 QgsMessageLog::logMessage( QObject::tr( "Error loading OpenCL program source from path %1" ).arg( QgsOpenClUtils::sourcePath() ), QgsOpenClUtils::LOGMESSAGE_TAG, Qgis::MessageLevel::Critical );
188 }
189 }
190
191#ifdef QGISDEBUG
192 std::chrono::time_point<std::chrono::system_clock> startTime( std::chrono::system_clock::now() );
193#endif
194
195 if ( useOpenCL )
196 {
197
198 try
199 {
200 std::size_t inputDataTypeSize = inputBlock->dataTypeSize();
201 std::size_t outputDataTypeSize = outputBlock->dataTypeSize();
202 // Buffer scanline, 1px height, 2px wider
203 QString typeName;
204 switch ( inputBlock->dataType() )
205 {
207 typeName = QStringLiteral( "unsigned char" );
208 break;
210 typeName = QStringLiteral( "unsigned int" );
211 break;
213 typeName = QStringLiteral( "short" );
214 break;
216 typeName = QStringLiteral( "unsigned int" );
217 break;
219 typeName = QStringLiteral( "int" );
220 break;
222 typeName = QStringLiteral( "float" );
223 break;
224 default:
225 throw QgsException( QStringLiteral( "Unsupported data type for OpenCL processing." ) );
226 }
227
228 if ( inputBlock->dataType() != Qgis::DataType::Float32 )
229 {
230 source.replace( QLatin1String( "__global float *scanLine" ), QStringLiteral( "__global %1 *scanLine" ).arg( typeName ) );
231 }
232
233 // Data type for input is Float32 (4 bytes)
234 std::size_t scanLineWidth( inputBlock->width() + 2 );
235 std::size_t inputSize( inputDataTypeSize * inputBlock->width() );
236
237 // CL buffers are also 2px wider for nodata initial and final columns
238 std::size_t bufferWidth( width + 2 );
239 std::size_t bufferSize( inputDataTypeSize * bufferWidth );
240
241 // Buffer scanlines, 1px height, 2px wider
242 // Data type for input is Float32 (4 bytes)
243 // keep only three scanlines in memory at a time, make room for initial and final nodata
244 auto scanLine = std::make_unique<QgsRasterBlock>( inputBlock->dataType(), scanLineWidth, 1 );
245 // Note: output block is not 2px wider and it is an image
246 // Prepare context and queue
247 cl::Context ctx = QgsOpenClUtils::context();
248 cl::CommandQueue queue = QgsOpenClUtils::commandQueue();
249
250 // Cast to float (because double just crashes on some GPUs)
251 std::vector<float> rasterParams;
252 rasterParams.push_back( inputBlock->noDataValue() );
253 rasterParams.push_back( outputBlock->noDataValue() );
254 rasterParams.push_back( mZFactor );
255 rasterParams.push_back( cellXSize );
256 rasterParams.push_back( cellYSize );
257 rasterParams.push_back( static_cast<float>( mOpacity ) ); // 5
258
259 // For fast formula from GDAL DEM
260 rasterParams.push_back( cos_az_mul_cos_alt_mul_z_mul_254 ); // 6
261 rasterParams.push_back( sin_az_mul_cos_alt_mul_z_mul_254 ); // 7
262 rasterParams.push_back( square_z ); // 8
263 rasterParams.push_back( sin_altRadians_mul_254 ); // 9
264
265 // For multidirectional fast formula
266 rasterParams.push_back( sin_altRadians_mul_127 ); // 10
267 rasterParams.push_back( cos225_az_mul_cos_alt_mul_z_mul_127 ); // 11
268 rasterParams.push_back( cos_alt_mul_z_mul_127 ); // 12
269
270 // Default color for nodata (BGR components)
271 rasterParams.push_back( static_cast<float>( qBlue( defaultNodataColor ) ) ); // 13
272 rasterParams.push_back( static_cast<float>( qGreen( defaultNodataColor ) ) ); // 14
273 rasterParams.push_back( static_cast<float>( qRed( defaultNodataColor ) ) ); // 15
274 rasterParams.push_back( static_cast<float>( qAlpha( defaultNodataColor ) ) / 255.0f ); // 16
275
276 // Whether use multidirectional
277 rasterParams.push_back( static_cast<float>( mMultiDirectional ) ); // 17
278
279 cl::Buffer rasterParamsBuffer( queue, rasterParams.begin(), rasterParams.end(), true, false, nullptr );
280 cl::Buffer scanLine1Buffer( ctx, CL_MEM_READ_ONLY, bufferSize, nullptr, nullptr );
281 cl::Buffer scanLine2Buffer( ctx, CL_MEM_READ_ONLY, bufferSize, nullptr, nullptr );
282 cl::Buffer scanLine3Buffer( ctx, CL_MEM_READ_ONLY, bufferSize, nullptr, nullptr );
283 cl::Buffer *scanLineBuffer[3] = {&scanLine1Buffer, &scanLine2Buffer, &scanLine3Buffer};
284 // Note that result buffer is an image
285 cl::Buffer resultLineBuffer( ctx, CL_MEM_WRITE_ONLY, outputDataTypeSize * width, nullptr, nullptr );
286
287 static std::map<Qgis::DataType, cl::Program> programCache;
288 cl::Program program = programCache[inputBlock->dataType()];
289 if ( ! program.get() )
290 {
291 // Create a program from the kernel source
292 programCache[inputBlock->dataType()] = QgsOpenClUtils::buildProgram( source, QgsOpenClUtils::ExceptionBehavior::Throw );
293 program = programCache[inputBlock->dataType()];
294 }
295
296 // Disable program cache when developing and testing cl program
297 // program = QgsOpenClUtils::buildProgram( ctx, source, QgsOpenClUtils::ExceptionBehavior::Throw );
298
299 // Create the OpenCL kernel
300 auto kernel = cl::KernelFunctor <
301 cl::Buffer &,
302 cl::Buffer &,
303 cl::Buffer &,
304 cl::Buffer &,
305 cl::Buffer &
306 > ( program, "processNineCellWindow" );
307
308
309 // Rotating buffer index
310 std::vector<int> rowIndex = {0, 1, 2};
311
312 for ( int i = 0; i < height; i++ )
313 {
314 if ( feedback && feedback->isCanceled() )
315 {
316 break;
317 }
318
319 if ( feedback )
320 {
321 feedback->setProgress( 100.0 * static_cast< double >( i ) / height );
322 }
323
324 if ( i == 0 )
325 {
326 // Fill scanline 1 with (input) nodata for the values above the first row and feed scanline2 with the first row
327 scanLine->resetNoDataValue();
328 queue.enqueueWriteBuffer( scanLine1Buffer, CL_TRUE, 0, bufferSize, scanLine->bits( ) );
329 // Read first row
330 memcpy( scanLine->bits( 0, 1 ), inputBlock->bits( i, 0 ), inputSize );
331 queue.enqueueWriteBuffer( scanLine2Buffer, CL_TRUE, 0, bufferSize, scanLine->bits( ) ); // row 0
332 // Second row
333 memcpy( scanLine->bits( 0, 1 ), inputBlock->bits( i + 1, 0 ), inputSize );
334 queue.enqueueWriteBuffer( scanLine3Buffer, CL_TRUE, 0, bufferSize, scanLine->bits( ) ); //
335 }
336 else
337 {
338 // Normally fetch only scanLine3 and move forward one row
339 // Read scanline 3, fill the last row with nodata values if it's the last iteration
340 if ( i == inputBlock->height() - 1 )
341 {
342 scanLine->resetNoDataValue();
343 queue.enqueueWriteBuffer( *scanLineBuffer[rowIndex[2]], CL_TRUE, 0, bufferSize, scanLine->bits( ) );
344 }
345 else // Overwrite from input, skip first and last
346 {
347 queue.enqueueWriteBuffer( *scanLineBuffer[rowIndex[2]], CL_TRUE, inputDataTypeSize * 1 /* offset 1 */, inputSize, inputBlock->bits( i + 1, 0 ) );
348 }
349 }
350
351 kernel( cl::EnqueueArgs(
352 queue,
353 cl::NDRange( width )
354 ),
355 *scanLineBuffer[rowIndex[0]],
356 *scanLineBuffer[rowIndex[1]],
357 *scanLineBuffer[rowIndex[2]],
358 resultLineBuffer,
359 rasterParamsBuffer
360 );
361
362 queue.enqueueReadBuffer( resultLineBuffer, CL_TRUE, 0, outputDataTypeSize * outputBlock->width( ), outputBlock->bits( i, 0 ) );
363 std::rotate( rowIndex.begin(), rowIndex.begin() + 1, rowIndex.end() );
364 }
365 }
366 catch ( cl::Error &e )
367 {
368 QgsMessageLog::logMessage( QObject::tr( "Error running OpenCL program: %1 - %2" ).arg( e.what( ) ).arg( QgsOpenClUtils::errorText( e.err( ) ) ),
371 QgsMessageLog::logMessage( QObject::tr( "OpenCL has been disabled, you can re-enable it in the options dialog." ),
373 }
374
375 } // End of OpenCL processing path
376 else // Use the CPU and the original algorithm
377 {
378
379#endif
380
381 double pixelValues[9] {0, 0, 0, 0, 0, 0, 0, 0, 0};
382 bool isNoData[9] {false, false, false, false, false, false, false, false, false};
383
384 for ( int row = 0; row < height; row++ )
385 {
386 for ( int col = 0; col < width; col++ )
387 {
388 int iUp = row - 1;
389 int iDown = row + 1;
390 if ( row == 0 )
391 {
392 iUp = row;
393 }
394 else if ( row == height - 1 )
395 {
396 iDown = row;
397 }
398
399 if ( col == 0 )
400 {
401 // seed the matrix with the values from the first column
402 pixelValues[ 0 ] = inputBlock->valueAndNoData( iUp, 0, isNoData[0] );
403 pixelValues[ 1 ] = pixelValues[0];
404 isNoData[1] = isNoData[0];
405 pixelValues[ 2 ] = pixelValues[0];
406 isNoData[2] = isNoData[0];
407
408 pixelValues[ 3 ] = inputBlock->valueAndNoData( row, 0, isNoData[3] );
409 pixelValues[ 4 ] = pixelValues[3];
410 isNoData[4] = isNoData[3];
411 pixelValues[ 5 ] = pixelValues[3];
412 isNoData[5] = isNoData[3];
413
414 pixelValues[ 6 ] = inputBlock->valueAndNoData( iDown, 0, isNoData[6] );
415 pixelValues[ 7 ] = pixelValues[6];
416 isNoData[7] = isNoData[6];
417 pixelValues[ 8 ] = pixelValues[6];
418 isNoData[8] = isNoData[6];
419 }
420 else
421 {
422 // shift matrices left
423 pixelValues[ 0 ] = pixelValues[1];
424 pixelValues[ 1 ] = pixelValues[2];
425 pixelValues[ 3 ] = pixelValues[4];
426 pixelValues[ 4 ] = pixelValues[5];
427 pixelValues[ 6 ] = pixelValues[7];
428 pixelValues[ 7 ] = pixelValues[8];
429 isNoData[ 0 ] = isNoData[1];
430 isNoData[ 1 ] = isNoData[2];
431 isNoData[ 3 ] = isNoData[4];
432 isNoData[ 4 ] = isNoData[5];
433 isNoData[ 6 ] = isNoData[7];
434 isNoData[ 7 ] = isNoData[8];
435 }
436
437 // calculate new values
438 if ( col < width - 1 )
439 {
440 pixelValues[2] = inputBlock->valueAndNoData( iUp, col + 1, isNoData[2] );
441 pixelValues[5] = inputBlock->valueAndNoData( row, col + 1, isNoData[5] );
442 pixelValues[8] = inputBlock->valueAndNoData( iDown, col + 1, isNoData[8] );
443 }
444
445 if ( isNoData[ 4 ] )
446 {
447 outputBlock->setColor( row, col, defaultNodataColor );
448 continue;
449 }
450
451 // This is center cell. Use this in place of nodata neighbors
452 const double x22 = pixelValues[4];
453
454 const double x11 = isNoData[0] ? x22 : pixelValues[0];
455 const double x21 = isNoData[3] ? x22 : pixelValues[3];
456 const double x31 = isNoData[6] ? x22 : pixelValues[6];
457 const double x12 = isNoData[1] ? x22 : pixelValues[1];
458 // x22
459 const double x32 = isNoData[7] ? x22 : pixelValues[7];
460 const double x13 = isNoData[2] ? x22 : pixelValues[2];
461 const double x23 = isNoData[5] ? x22 : pixelValues[5];
462 const double x33 = isNoData[8] ? x22 : pixelValues[8];
463
464 // Calculates the first order derivative in x-direction according to Horn (1981)
465 const double derX = ( ( x13 + x23 + x23 + x33 ) - ( x11 + x21 + x21 + x31 ) ) / ( 8 * cellXSize );
466 const double derY = ( ( x31 + x32 + x32 + x33 ) - ( x11 + x12 + x12 + x13 ) ) / ( 8 * -cellYSize );
467
468 // Fast formula
469
470 double grayValue;
471 if ( !mMultiDirectional )
472 {
473 // Standard single direction hillshade
474 grayValue = std::clamp( ( sin_altRadians_mul_254 -
475 ( derY * cos_az_mul_cos_alt_mul_z_mul_254 -
476 derX * sin_az_mul_cos_alt_mul_z_mul_254 ) ) /
477 std::sqrt( 1 + square_z * ( derX * derX + derY * derY ) ),
478 0.0, 255.0 );
479 }
480 else
481 {
482 // Weighted multi direction as in http://pubs.usgs.gov/of/1992/of92-422/of92-422.pdf
483 // Fast formula from GDAL DEM
484 const float xx = derX * derX;
485 const float yy = derY * derY;
486 const float xx_plus_yy = xx + yy;
487 // Flat?
488 if ( xx_plus_yy == 0.0 )
489 {
490 grayValue = std::clamp( static_cast<float>( 1.0 + sin_altRadians_mul_254 ), 0.0f, 255.0f );
491 }
492 else
493 {
494 // ... then the shade value from different azimuth
495 float val225_mul_127 = sin_altRadians_mul_127 +
496 ( derX - derY ) * cos225_az_mul_cos_alt_mul_z_mul_127;
497 val225_mul_127 = ( val225_mul_127 <= 0.0 ) ? 0.0 : val225_mul_127;
498 float val270_mul_127 = sin_altRadians_mul_127 -
499 derX * cos_alt_mul_z_mul_127;
500 val270_mul_127 = ( val270_mul_127 <= 0.0 ) ? 0.0 : val270_mul_127;
501 float val315_mul_127 = sin_altRadians_mul_127 +
502 ( derX + derY ) * cos225_az_mul_cos_alt_mul_z_mul_127;
503 val315_mul_127 = ( val315_mul_127 <= 0.0 ) ? 0.0 : val315_mul_127;
504 float val360_mul_127 = sin_altRadians_mul_127 -
505 derY * cos_alt_mul_z_mul_127;
506 val360_mul_127 = ( val360_mul_127 <= 0.0 ) ? 0.0 : val360_mul_127;
507
508 // ... then the weighted shading
509 const float weight_225 = 0.5 * xx_plus_yy - derX * derY;
510 const float weight_270 = xx;
511 const float weight_315 = xx_plus_yy - weight_225;
512 const float weight_360 = yy;
513 const float cang_mul_127 = (
514 ( weight_225 * val225_mul_127 +
515 weight_270 * val270_mul_127 +
516 weight_315 * val315_mul_127 +
517 weight_360 * val360_mul_127 ) / xx_plus_yy ) /
518 ( 1 + square_z * xx_plus_yy );
519
520 grayValue = std::clamp( 1.0f + cang_mul_127, 0.0f, 255.0f );
521 }
522 }
523
524 double currentAlpha = mOpacity;
526 {
527 currentAlpha *= mRasterTransparency->opacityForValue( x22 );
528 }
529 if ( mAlphaBand > 0 )
530 {
531 currentAlpha *= alphaBlock->value( static_cast<qgssize>( row ) * width + col ) / 255.0;
532 }
533
534 if ( qgsDoubleNear( currentAlpha, 1.0 ) )
535 {
536 outputBlock->setColor( row, col, qRgba( grayValue, grayValue, grayValue, 255 ) );
537 }
538 else
539 {
540 outputBlock->setColor( row, col, qRgba( currentAlpha * grayValue, currentAlpha * grayValue, currentAlpha * grayValue, currentAlpha * 255 ) );
541 }
542 }
543 }
544
545#ifdef HAVE_OPENCL
546 } // End of switch in case OpenCL is not available or enabled
547
548#ifdef QGISDEBUG
549 if ( QgsSettings().value( QStringLiteral( "Map/logCanvasRefreshEvent" ), false ).toBool() )
550 {
551 QgsMessageLog::logMessage( QStringLiteral( "%1 processing time for hillshade (%2 x %3 ): %4 ms" )
552 .arg( useOpenCL ? QStringLiteral( "OpenCL" ) : QStringLiteral( "CPU" ) )
553 .arg( width )
554 .arg( height )
555 .arg( std::chrono::duration_cast<std::chrono::milliseconds>( std::chrono::system_clock::now() - startTime ).count() ),
556 tr( "Rendering" ) );
557 }
558#endif
559#endif
560
561 return outputBlock.release();
562}
563
565{
566 QList<int> bandList;
567 if ( mBand != -1 )
568 {
569 bandList << mBand;
570 }
571 return bandList;
572
573}
574
576{
577 return mBand;
578}
579
581{
582 setInputBand( bandNo );
583}
584
586{
587 if ( !mInput )
588 {
589 mBand = band;
590 return true;
591 }
592 else if ( band > 0 && band <= mInput->bandCount() )
593 {
594 mBand = band;
595 return true;
596 }
597 return false;
598}
599
600void QgsHillshadeRenderer::toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
601{
602 QgsSldExportContext context;
603 context.setExtraProperties( props );
604 toSld( doc, element, context );
605}
606
607bool QgsHillshadeRenderer::toSld( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
608{
609 // create base structure
610 QgsRasterRenderer::toSld( doc, element, context );
611
612 // look for RasterSymbolizer tag
613 QDomNodeList elements = element.elementsByTagName( QStringLiteral( "sld:RasterSymbolizer" ) );
614 if ( elements.size() == 0 )
615 return false;
616
617 // there SHOULD be only one
618 QDomElement rasterSymbolizerElem = elements.at( 0 ).toElement();
619
620 // add Channel Selection tags (if band is not default 1)
621 // Need to insert channelSelection in the correct sequence as in SLD standard e.g.
622 // after opacity or geometry or as first element after sld:RasterSymbolizer
623 if ( mBand != 1 )
624 {
625 QDomElement channelSelectionElem = doc.createElement( QStringLiteral( "sld:ChannelSelection" ) );
626 elements = rasterSymbolizerElem.elementsByTagName( QStringLiteral( "sld:Opacity" ) );
627 if ( elements.size() != 0 )
628 {
629 rasterSymbolizerElem.insertAfter( channelSelectionElem, elements.at( 0 ) );
630 }
631 else
632 {
633 elements = rasterSymbolizerElem.elementsByTagName( QStringLiteral( "sld:Geometry" ) );
634 if ( elements.size() != 0 )
635 {
636 rasterSymbolizerElem.insertAfter( channelSelectionElem, elements.at( 0 ) );
637 }
638 else
639 {
640 rasterSymbolizerElem.insertBefore( channelSelectionElem, rasterSymbolizerElem.firstChild() );
641 }
642 }
643
644 // for gray band
645 QDomElement channelElem = doc.createElement( QStringLiteral( "sld:GrayChannel" ) );
646 channelSelectionElem.appendChild( channelElem );
647
648 // set band
649 QDomElement sourceChannelNameElem = doc.createElement( QStringLiteral( "sld:SourceChannelName" ) );
650 sourceChannelNameElem.appendChild( doc.createTextNode( QString::number( mBand ) ) );
651 channelElem.appendChild( sourceChannelNameElem );
652 }
653
654 // add ShadedRelief tag
655 QDomElement shadedReliefElem = doc.createElement( QStringLiteral( "sld:ShadedRelief" ) );
656 rasterSymbolizerElem.appendChild( shadedReliefElem );
657
658 // brightnessOnly tag
659 QDomElement brightnessOnlyElem = doc.createElement( QStringLiteral( "sld:BrightnessOnly" ) );
660 brightnessOnlyElem.appendChild( doc.createTextNode( QStringLiteral( "true" ) ) );
661 shadedReliefElem.appendChild( brightnessOnlyElem );
662
663 // ReliefFactor tag
664 QDomElement reliefFactorElem = doc.createElement( QStringLiteral( "sld:ReliefFactor" ) );
665 reliefFactorElem.appendChild( doc.createTextNode( QString::number( zFactor() ) ) );
666 shadedReliefElem.appendChild( reliefFactorElem );
667
668 // altitude VendorOption tag
669 QDomElement altitudeVendorOptionElem = doc.createElement( QStringLiteral( "sld:VendorOption" ) );
670 altitudeVendorOptionElem.setAttribute( QStringLiteral( "name" ), QStringLiteral( "altitude" ) );
671 altitudeVendorOptionElem.appendChild( doc.createTextNode( QString::number( altitude() ) ) );
672 shadedReliefElem.appendChild( altitudeVendorOptionElem );
673
674 // azimuth VendorOption tag
675 QDomElement azimutVendorOptionElem = doc.createElement( QStringLiteral( "sld:VendorOption" ) );
676 azimutVendorOptionElem.setAttribute( QStringLiteral( "name" ), QStringLiteral( "azimuth" ) );
677 azimutVendorOptionElem.appendChild( doc.createTextNode( QString::number( azimuth() ) ) );
678 shadedReliefElem.appendChild( azimutVendorOptionElem );
679
680 // multidirectional VendorOption tag
681 QDomElement multidirectionalVendorOptionElem = doc.createElement( QStringLiteral( "sld:VendorOption" ) );
682 multidirectionalVendorOptionElem.setAttribute( QStringLiteral( "name" ), QStringLiteral( "multidirectional" ) );
683 multidirectionalVendorOptionElem.appendChild( doc.createTextNode( QString::number( multiDirectional() ) ) );
684 shadedReliefElem.appendChild( multidirectionalVendorOptionElem );
685
686 return true;
687}
QFlags< RasterRendererFlag > RasterRendererFlags
Flags which control behavior of raster renderers.
Definition qgis.h:1512
@ Critical
Critical/error message.
Definition qgis.h:159
@ InternalLayerOpacityHandling
The renderer internally handles the raster layer's opacity, so the default layer level opacity handli...
Definition qgis.h:1503
@ Float32
Thirty two bit floating point (float).
Definition qgis.h:380
@ Int16
Sixteen bit signed integer (qint16).
Definition qgis.h:377
@ ARGB32_Premultiplied
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
Definition qgis.h:387
@ UInt16
Sixteen bit unsigned integer (quint16).
Definition qgis.h:376
@ Byte
Eight bit unsigned integer (quint8).
Definition qgis.h:374
@ Int32
Thirty two bit signed integer (qint32).
Definition qgis.h:379
@ UInt32
Thirty two bit unsigned integer (quint32).
Definition qgis.h:378
Defines a QGIS exception class.
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:53
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition qgsfeedback.h:61
static QgsRasterRenderer * create(const QDomElement &elem, QgsRasterInterface *input)
Factory method to create a new renderer.
void writeXml(QDomDocument &doc, QDomElement &parentElem) const override
Write base class members to xml.
QgsHillshadeRenderer(QgsRasterInterface *input, int band, double lightAzimuth, double lightAltitude)
A renderer for generating live hillshade models.
bool multiDirectional() const
Returns true if the renderer is using multi-directional hillshading.
void setZFactor(double zfactor)
Set the Z scaling factor of the result image.
Q_DECL_DEPRECATED int band() const
Returns the band used by the renderer.
double azimuth() const
Returns the direction of the light over the raster between 0-360.
Q_DECL_DEPRECATED void toSld(QDomDocument &doc, QDomElement &element, const QVariantMap &props=QVariantMap()) const override
Used from subclasses to create SLD Rule elements following SLD v1.0 specs.
Qgis::RasterRendererFlags flags() const override
Returns flags which dictate renderer behavior.
Q_DECL_DEPRECATED void setBand(int bandNo)
Sets the band used by the renderer.
bool setInputBand(int band) override
Attempts to set the input band for the renderer.
int inputBand() const override
Returns the input band for the renderer, or -1 if no input band is available.
void setMultiDirectional(bool isMultiDirectional)
Sets whether to render using a multi-directional hillshade algorithm.
QList< int > usesBands() const override
Returns a list of band numbers used by the renderer.
QgsRasterBlock * block(int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback *feedback=nullptr) override
Read block of data using given extent and size.
double altitude() const
Returns the angle of the light source over the raster.
double zFactor() const
Returns the Z scaling factor.
QgsHillshadeRenderer * clone() const override
Clone itself, create deep copy.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE())
Adds a message to the log instance (and creates it if necessary).
static Q_DECL_DEPRECATED cl::Program buildProgram(const cl::Context &context, const QString &source, ExceptionBehavior exceptionBehavior=Catch)
Build the program from source in the given context and depending on exceptionBehavior can throw or ca...
static QString sourcePath()
Returns the base path to OpenCL program directory.
static cl::Context context()
Context factory.
static bool enabled()
Returns true if OpenCL is enabled in the user settings.
static bool available()
Checks whether a suitable OpenCL platform and device is available on this system and initialize the Q...
static void setEnabled(bool enabled)
Set the OpenCL user setting to enabled.
static QString errorText(const int errorCode)
Returns a string representation from an OpenCL errorCode.
static cl::CommandQueue commandQueue()
Create an OpenCL command queue from the default context.
static QString sourceFromBaseName(const QString &baseName)
Returns the full path to a an OpenCL source file from the baseName ('.cl' extension is automatically ...
@ Throw
Write errors in the message log and re-throw exceptions.
static QLatin1String LOGMESSAGE_TAG
OpenCL string for message logs.
Feedback object tailored for raster block reading.
Raster data container.
QgsRasterInterface(QgsRasterInterface *input=nullptr)
QgsRasterInterface * mInput
virtual QgsRectangle extent() const
Gets the extent of the interface.
virtual QgsRasterInterface * input() const
Current input.
QgsRasterRenderer(QgsRasterInterface *input=nullptr, const QString &type=QString())
Constructor for QgsRasterRenderer.
double mOpacity
Global alpha value (0-1).
int mAlphaBand
Read alpha value from band.
QRgb renderColorForNodataPixel() const
Returns the color for the renderer to use to represent nodata pixels.
std::unique_ptr< QgsRasterTransparency > mRasterTransparency
Raster transparency per color or value. Overwrites global alpha value.
void _writeXml(QDomDocument &doc, QDomElement &rasterRendererElem) const
Write upper class info into rasterrenderer element (called by writeXml method of subclasses).
int bandCount() const override
Gets number of bands.
void copyCommonProperties(const QgsRasterRenderer *other, bool copyMinMaxOrigin=true)
Copies common properties like opacity / transparency data from other renderer.
virtual Q_DECL_DEPRECATED void toSld(QDomDocument &doc, QDomElement &element, const QVariantMap &props=QVariantMap()) const
Used from subclasses to create SLD Rule elements following SLD v1.0 specs.
void readXml(const QDomElement &rendererElem) override
Sets base class members from xml. Usually called from create() methods of subclasses.
A rectangle specified with double values.
Stores settings for use within QGIS.
Definition qgssettings.h:65
Holds SLD export options and other information related to SLD export of a QGIS layer style.
void setExtraProperties(const QVariantMap &properties)
Sets the open ended set of properties that can drive/inform the SLD encoding.
unsigned long long qgssize
Qgssize is used instead of size_t, because size_t is stdlib type, unknown by SIP, and it would be har...
Definition qgis.h:7142
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6607
#define QgsDebugError(str)
Definition qgslogger.h:57