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