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