QGIS API Documentation 4.1.0-Master (009143bf4b4)
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#include <QString>
31
32using namespace Qt::StringLiterals;
33
34#ifdef HAVE_OPENCL
35#ifdef QGISDEBUG
36#include <chrono>
37#include "qgsmaprendererjob.h"
39#endif
40#include "qgsexception.h"
41#include "qgsopenclutils.h"
42#endif
43
44QgsHillshadeRenderer::QgsHillshadeRenderer( QgsRasterInterface *input, int band, double lightAzimuth, double lightAngle )
45 : QgsRasterRenderer( input, u"hillshade"_s )
46 , mBand( band )
47 , mLightAngle( lightAngle )
48 , mLightAzimuth( lightAzimuth )
49{}
50
52{
53 QgsHillshadeRenderer *r = new QgsHillshadeRenderer( nullptr, mBand, mLightAzimuth, mLightAngle );
54 r->copyCommonProperties( this );
55
56 r->setZFactor( mZFactor );
57 r->setMultiDirectional( mMultiDirectional );
58 return r;
59}
60
65
67{
68 if ( elem.isNull() )
69 {
70 return nullptr;
71 }
72
73 int band = elem.attribute( u"band"_s, u"0"_s ).toInt();
74 double azimuth = elem.attribute( u"azimuth"_s, u"315"_s ).toDouble();
75 double angle = elem.attribute( u"angle"_s, u"45"_s ).toDouble();
76 double zFactor = elem.attribute( u"zfactor"_s, u"1"_s ).toDouble();
77 bool multiDirectional = elem.attribute( u"multidirection"_s, u"0"_s ).toInt();
79 r->readXml( elem );
80
81 r->setZFactor( zFactor );
83 return r;
84}
85
86void QgsHillshadeRenderer::writeXml( QDomDocument &doc, QDomElement &parentElem ) const
87{
88 if ( parentElem.isNull() )
89 {
90 return;
91 }
92
93 QDomElement rasterRendererElem = doc.createElement( u"rasterrenderer"_s );
94 _writeXml( doc, rasterRendererElem );
95
96 rasterRendererElem.setAttribute( u"band"_s, mBand );
97 rasterRendererElem.setAttribute( u"azimuth"_s, QString::number( mLightAzimuth ) );
98 rasterRendererElem.setAttribute( u"angle"_s, QString::number( mLightAngle ) );
99 rasterRendererElem.setAttribute( u"zfactor"_s, QString::number( mZFactor ) );
100 rasterRendererElem.setAttribute( u"multidirection"_s, QString::number( mMultiDirectional ) );
101 parentElem.appendChild( rasterRendererElem );
102}
103
104QgsRasterBlock *QgsHillshadeRenderer::block( int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback *feedback )
105{
106 Q_UNUSED( bandNo )
107 auto outputBlock = std::make_unique<QgsRasterBlock>();
108 if ( !mInput )
109 {
110 QgsDebugError( u"No input raster!"_s );
111 return outputBlock.release();
112 }
113
114 std::shared_ptr< QgsRasterBlock > inputBlock( mInput->block( mBand, extent, width, height, feedback ) );
115
116 if ( !inputBlock || inputBlock->isEmpty() )
117 {
118 QgsDebugError( u"No raster data!"_s );
119 return outputBlock.release();
120 }
121
122 std::shared_ptr< QgsRasterBlock > alphaBlock;
123
124 if ( mAlphaBand > 0 && mBand != mAlphaBand )
125 {
126 alphaBlock.reset( mInput->block( mAlphaBand, extent, width, height, feedback ) );
127 if ( !alphaBlock || alphaBlock->isEmpty() )
128 {
129 // TODO: better to render without alpha
130 return outputBlock.release();
131 }
132 }
133 else if ( mAlphaBand > 0 )
134 {
135 alphaBlock = inputBlock;
136 }
137
138 if ( !outputBlock->reset( Qgis::DataType::ARGB32_Premultiplied, width, height ) )
139 {
140 return outputBlock.release();
141 }
142
143 if ( width == 0 || height == 0 )
144 return outputBlock.release();
145
146 // Starting the computation
147
148 // Common pre-calculated values
149 float cellXSize = static_cast<float>( extent.width() ) / width;
150 float cellYSize = static_cast<float>( extent.height() ) / height;
151 float zenithRad = static_cast<float>( std::max( 0.0, 90 - mLightAngle ) * M_PI / 180.0 );
152 float azimuthRad = static_cast<float>( -1 * mLightAzimuth * M_PI / 180.0 );
153 float cosZenithRad = std::cos( zenithRad );
154 float sinZenithRad = std::sin( zenithRad );
155
156 // For fast formula from GDAL DEM
157 float cos_alt_mul_z = cosZenithRad * static_cast<float>( mZFactor );
158 float cos_az_mul_cos_alt_mul_z = std::cos( azimuthRad ) * cos_alt_mul_z;
159 float sin_az_mul_cos_alt_mul_z = std::sin( azimuthRad ) * cos_alt_mul_z;
160 float cos_az_mul_cos_alt_mul_z_mul_254 = 254.0f * cos_az_mul_cos_alt_mul_z;
161 float sin_az_mul_cos_alt_mul_z_mul_254 = 254.0f * sin_az_mul_cos_alt_mul_z;
162 float square_z = static_cast<float>( mZFactor * mZFactor );
163 float sin_altRadians_mul_254 = 254.0f * sinZenithRad;
164
165 // For multi directional
166 float sin_altRadians_mul_127 = 127.0f * sinZenithRad;
167 // 127.0 * std::cos(225.0 * M_PI / 180.0) = -32.87001872802012
168 float cos225_az_mul_cos_alt_mul_z_mul_127 = -32.87001872802012f * cos_alt_mul_z;
169 float cos_alt_mul_z_mul_127 = 127.0f * cos_alt_mul_z;
170
171 const QRgb defaultNodataColor = renderColorForNodataPixel();
172
173#ifdef HAVE_OPENCL
174
175 // Use OpenCL? For now OpenCL is enabled in the default configuration only
177 // Check for sources
178 QString source;
179 if ( useOpenCL )
180 {
181 source = QgsOpenClUtils::sourceFromBaseName( u"hillshade_renderer"_s );
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 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 = u"unsigned char"_s;
205 break;
207 typeName = u"unsigned int"_s;
208 break;
210 typeName = u"short"_s;
211 break;
213 typeName = u"unsigned int"_s;
214 break;
216 typeName = u"int"_s;
217 break;
219 typeName = u"float"_s;
220 break;
221 default:
222 throw QgsException( u"Unsupported data type for OpenCL processing."_s );
223 }
224
225 if ( inputBlock->dataType() != Qgis::DataType::Float32 )
226 {
227 source.replace( "__global float *scanLine"_L1, u"__global %1 *scanLine"_s.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 auto 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< cl::Buffer &, cl::Buffer &, cl::Buffer &, cl::Buffer &, cl::Buffer & >( program, "processNineCellWindow" );
298
299
300 // Rotating buffer index
301 std::vector<int> rowIndex = { 0, 1, 2 };
302
303 for ( int i = 0; i < height; i++ )
304 {
305 if ( feedback && feedback->isCanceled() )
306 {
307 break;
308 }
309
310 if ( feedback )
311 {
312 feedback->setProgress( 100.0 * static_cast< double >( i ) / height );
313 }
314
315 if ( i == 0 )
316 {
317 // Fill scanline 1 with (input) nodata for the values above the first row and feed scanline2 with the first row
318 scanLine->resetNoDataValue();
319 queue.enqueueWriteBuffer( scanLine1Buffer, CL_TRUE, 0, bufferSize, scanLine->bits() );
320 // Read first row
321 memcpy( scanLine->bits( 0, 1 ), inputBlock->bits( i, 0 ), inputSize );
322 queue.enqueueWriteBuffer( scanLine2Buffer, CL_TRUE, 0, bufferSize, scanLine->bits() ); // row 0
323 // Second row
324 memcpy( scanLine->bits( 0, 1 ), inputBlock->bits( i + 1, 0 ), inputSize );
325 queue.enqueueWriteBuffer( scanLine3Buffer, CL_TRUE, 0, bufferSize, scanLine->bits() ); //
326 }
327 else
328 {
329 // Normally fetch only scanLine3 and move forward one row
330 // Read scanline 3, fill the last row with nodata values if it's the last iteration
331 if ( i == inputBlock->height() - 1 )
332 {
333 scanLine->resetNoDataValue();
334 queue.enqueueWriteBuffer( *scanLineBuffer[rowIndex[2]], CL_TRUE, 0, bufferSize, scanLine->bits() );
335 }
336 else // Overwrite from input, skip first and last
337 {
338 queue.enqueueWriteBuffer( *scanLineBuffer[rowIndex[2]], CL_TRUE, inputDataTypeSize * 1 /* offset 1 */, inputSize, inputBlock->bits( i + 1, 0 ) );
339 }
340 }
341
342 kernel( cl::EnqueueArgs( queue, cl::NDRange( width ) ), *scanLineBuffer[rowIndex[0]], *scanLineBuffer[rowIndex[1]], *scanLineBuffer[rowIndex[2]], resultLineBuffer, rasterParamsBuffer );
343
344 queue.enqueueReadBuffer( resultLineBuffer, CL_TRUE, 0, outputDataTypeSize * outputBlock->width(), outputBlock->bits( i, 0 ) );
345 std::rotate( rowIndex.begin(), rowIndex.begin() + 1, rowIndex.end() );
346 }
347 }
348 catch ( cl::Error &e )
349 {
350 QgsMessageLog::logMessage( QObject::tr( "Error running OpenCL program: %1 - %2" ).arg( e.what() ).arg( QgsOpenClUtils::errorText( e.err() ) ), QgsOpenClUtils::LOGMESSAGE_TAG, Qgis::MessageLevel::Critical );
352 QgsMessageLog::logMessage( QObject::tr( "OpenCL has been disabled, you can re-enable it in the options dialog." ), QgsOpenClUtils::LOGMESSAGE_TAG, Qgis::MessageLevel::Critical );
353 }
354
355 } // End of OpenCL processing path
356 else // Use the CPU and the original algorithm
357 {
358#endif
359
360 double pixelValues[9] { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
361 bool isNoData[9] { false, false, false, false, false, false, false, false, false };
362
363 for ( int row = 0; row < height; row++ )
364 {
365 for ( int col = 0; col < width; col++ )
366 {
367 int iUp = row - 1;
368 int iDown = row + 1;
369 if ( row == 0 )
370 {
371 iUp = row;
372 }
373 else if ( row == height - 1 )
374 {
375 iDown = row;
376 }
377
378 if ( col == 0 )
379 {
380 // seed the matrix with the values from the first column
381 pixelValues[0] = inputBlock->valueAndNoData( iUp, 0, isNoData[0] );
382 pixelValues[1] = pixelValues[0];
383 isNoData[1] = isNoData[0];
384 pixelValues[2] = pixelValues[0];
385 isNoData[2] = isNoData[0];
386
387 pixelValues[3] = inputBlock->valueAndNoData( row, 0, isNoData[3] );
388 pixelValues[4] = pixelValues[3];
389 isNoData[4] = isNoData[3];
390 pixelValues[5] = pixelValues[3];
391 isNoData[5] = isNoData[3];
392
393 pixelValues[6] = inputBlock->valueAndNoData( iDown, 0, isNoData[6] );
394 pixelValues[7] = pixelValues[6];
395 isNoData[7] = isNoData[6];
396 pixelValues[8] = pixelValues[6];
397 isNoData[8] = isNoData[6];
398 }
399 else
400 {
401 // shift matrices left
402 pixelValues[0] = pixelValues[1];
403 pixelValues[1] = pixelValues[2];
404 pixelValues[3] = pixelValues[4];
405 pixelValues[4] = pixelValues[5];
406 pixelValues[6] = pixelValues[7];
407 pixelValues[7] = pixelValues[8];
408 isNoData[0] = isNoData[1];
409 isNoData[1] = isNoData[2];
410 isNoData[3] = isNoData[4];
411 isNoData[4] = isNoData[5];
412 isNoData[6] = isNoData[7];
413 isNoData[7] = isNoData[8];
414 }
415
416 // calculate new values
417 if ( col < width - 1 )
418 {
419 pixelValues[2] = inputBlock->valueAndNoData( iUp, col + 1, isNoData[2] );
420 pixelValues[5] = inputBlock->valueAndNoData( row, col + 1, isNoData[5] );
421 pixelValues[8] = inputBlock->valueAndNoData( iDown, col + 1, isNoData[8] );
422 }
423
424 if ( isNoData[4] )
425 {
426 outputBlock->setColor( row, col, defaultNodataColor );
427 continue;
428 }
429
430 // This is center cell. Use this in place of nodata neighbors
431 const double x22 = pixelValues[4];
432
433 const double x11 = isNoData[0] ? x22 : pixelValues[0];
434 const double x21 = isNoData[3] ? x22 : pixelValues[3];
435 const double x31 = isNoData[6] ? x22 : pixelValues[6];
436 const double x12 = isNoData[1] ? x22 : pixelValues[1];
437 // x22
438 const double x32 = isNoData[7] ? x22 : pixelValues[7];
439 const double x13 = isNoData[2] ? x22 : pixelValues[2];
440 const double x23 = isNoData[5] ? x22 : pixelValues[5];
441 const double x33 = isNoData[8] ? x22 : pixelValues[8];
442
443 // Calculates the first order derivative in x-direction according to Horn (1981)
444 const double derX = ( ( x13 + x23 + x23 + x33 ) - ( x11 + x21 + x21 + x31 ) ) / ( 8 * cellXSize );
445 const double derY = ( ( x31 + x32 + x32 + x33 ) - ( x11 + x12 + x12 + x13 ) ) / ( 8 * -cellYSize );
446
447 // Fast formula
448
449 double grayValue;
450 if ( !mMultiDirectional )
451 {
452 // Standard single direction hillshade
453 grayValue
454 = std::clamp( ( sin_altRadians_mul_254 - ( derY * cos_az_mul_cos_alt_mul_z_mul_254 - derX * sin_az_mul_cos_alt_mul_z_mul_254 ) ) / std::sqrt( 1 + square_z * ( derX * derX + derY * derY ) ), 0.0, 255.0 );
455 }
456 else
457 {
458 // Weighted multi direction as in http://pubs.usgs.gov/of/1992/of92-422/of92-422.pdf
459 // Fast formula from GDAL DEM
460 const float xx = derX * derX;
461 const float yy = derY * derY;
462 const float xx_plus_yy = xx + yy;
463 // Flat?
464 if ( xx_plus_yy == 0.0 )
465 {
466 grayValue = std::clamp( static_cast<float>( 1.0 + sin_altRadians_mul_254 ), 0.0f, 255.0f );
467 }
468 else
469 {
470 // ... then the shade value from different azimuth
471 float val225_mul_127 = sin_altRadians_mul_127 + ( derX - derY ) * cos225_az_mul_cos_alt_mul_z_mul_127;
472 val225_mul_127 = ( val225_mul_127 <= 0.0 ) ? 0.0 : val225_mul_127;
473 float val270_mul_127 = sin_altRadians_mul_127 - derX * cos_alt_mul_z_mul_127;
474 val270_mul_127 = ( val270_mul_127 <= 0.0 ) ? 0.0 : val270_mul_127;
475 float val315_mul_127 = sin_altRadians_mul_127 + ( derX + derY ) * cos225_az_mul_cos_alt_mul_z_mul_127;
476 val315_mul_127 = ( val315_mul_127 <= 0.0 ) ? 0.0 : val315_mul_127;
477 float val360_mul_127 = sin_altRadians_mul_127 - derY * cos_alt_mul_z_mul_127;
478 val360_mul_127 = ( val360_mul_127 <= 0.0 ) ? 0.0 : val360_mul_127;
479
480 // ... then the weighted shading
481 const float weight_225 = 0.5 * xx_plus_yy - derX * derY;
482 const float weight_270 = xx;
483 const float weight_315 = xx_plus_yy - weight_225;
484 const float weight_360 = yy;
485 const float cang_mul_127 = ( ( weight_225 * val225_mul_127 + weight_270 * val270_mul_127 + weight_315 * val315_mul_127 + weight_360 * val360_mul_127 ) / xx_plus_yy )
486 / ( 1 + square_z * xx_plus_yy );
487
488 grayValue = std::clamp( 1.0f + cang_mul_127, 0.0f, 255.0f );
489 }
490 }
491
492 double currentAlpha = mOpacity;
494 {
495 currentAlpha *= mRasterTransparency->opacityForValue( x22 );
496 }
497 if ( mAlphaBand > 0 )
498 {
499 currentAlpha *= alphaBlock->value( static_cast<qgssize>( row ) * width + col ) / 255.0;
500 }
501
502 if ( qgsDoubleNear( currentAlpha, 1.0 ) )
503 {
504 outputBlock->setColor( row, col, qRgba( grayValue, grayValue, grayValue, 255 ) );
505 }
506 else
507 {
508 outputBlock->setColor( row, col, qRgba( currentAlpha * grayValue, currentAlpha * grayValue, currentAlpha * grayValue, currentAlpha * 255 ) );
509 }
510 }
511 }
512
513#ifdef HAVE_OPENCL
514 } // End of switch in case OpenCL is not available or enabled
515
516#ifdef QGISDEBUG
518 {
520 u"%1 processing time for hillshade (%2 x %3 ): %4 ms"_s.arg( useOpenCL ? u"OpenCL"_s : u"CPU"_s )
521 .arg( width )
522 .arg( height )
523 .arg( std::chrono::duration_cast<std::chrono::milliseconds>( std::chrono::system_clock::now() - startTime ).count() ),
524 tr( "Rendering" )
525 );
526 }
527#endif
528#endif
529
530 return outputBlock.release();
531}
532
534{
535 QList<int> bandList;
536 if ( mBand != -1 )
537 {
538 bandList << mBand;
539 }
540 return bandList;
541}
542
544{
545 return mBand;
546}
547
549{
550 setInputBand( bandNo );
551}
552
554{
555 if ( !mInput )
556 {
557 mBand = band;
558 return true;
559 }
560 else if ( band > 0 && band <= mInput->bandCount() )
561 {
562 mBand = band;
563 return true;
564 }
565 return false;
566}
567
568void QgsHillshadeRenderer::toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
569{
570 QgsSldExportContext context;
571 context.setExtraProperties( props );
572 toSld( doc, element, context );
573}
574
575bool QgsHillshadeRenderer::toSld( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
576{
577 // create base structure
578 QgsRasterRenderer::toSld( doc, element, context );
579
580 // look for RasterSymbolizer tag
581 QDomNodeList elements = element.elementsByTagName( u"sld:RasterSymbolizer"_s );
582 if ( elements.size() == 0 )
583 return false;
584
585 // there SHOULD be only one
586 QDomElement rasterSymbolizerElem = elements.at( 0 ).toElement();
587
588 // add Channel Selection tags (if band is not default 1)
589 // Need to insert channelSelection in the correct sequence as in SLD standard e.g.
590 // after opacity or geometry or as first element after sld:RasterSymbolizer
591 if ( mBand != 1 )
592 {
593 QDomElement channelSelectionElem = doc.createElement( u"sld:ChannelSelection"_s );
594 elements = rasterSymbolizerElem.elementsByTagName( u"sld:Opacity"_s );
595 if ( elements.size() != 0 )
596 {
597 rasterSymbolizerElem.insertAfter( channelSelectionElem, elements.at( 0 ) );
598 }
599 else
600 {
601 elements = rasterSymbolizerElem.elementsByTagName( u"sld:Geometry"_s );
602 if ( elements.size() != 0 )
603 {
604 rasterSymbolizerElem.insertAfter( channelSelectionElem, elements.at( 0 ) );
605 }
606 else
607 {
608 rasterSymbolizerElem.insertBefore( channelSelectionElem, rasterSymbolizerElem.firstChild() );
609 }
610 }
611
612 // for gray band
613 QDomElement channelElem = doc.createElement( u"sld:GrayChannel"_s );
614 channelSelectionElem.appendChild( channelElem );
615
616 // set band
617 QDomElement sourceChannelNameElem = doc.createElement( u"sld:SourceChannelName"_s );
618 sourceChannelNameElem.appendChild( doc.createTextNode( QString::number( mBand ) ) );
619 channelElem.appendChild( sourceChannelNameElem );
620 }
621
622 // add ShadedRelief tag
623 QDomElement shadedReliefElem = doc.createElement( u"sld:ShadedRelief"_s );
624 rasterSymbolizerElem.appendChild( shadedReliefElem );
625
626 // brightnessOnly tag
627 QDomElement brightnessOnlyElem = doc.createElement( u"sld:BrightnessOnly"_s );
628 brightnessOnlyElem.appendChild( doc.createTextNode( u"true"_s ) );
629 shadedReliefElem.appendChild( brightnessOnlyElem );
630
631 // ReliefFactor tag
632 QDomElement reliefFactorElem = doc.createElement( u"sld:ReliefFactor"_s );
633 reliefFactorElem.appendChild( doc.createTextNode( QString::number( zFactor() ) ) );
634 shadedReliefElem.appendChild( reliefFactorElem );
635
636 // altitude VendorOption tag
637 QDomElement altitudeVendorOptionElem = doc.createElement( u"sld:VendorOption"_s );
638 altitudeVendorOptionElem.setAttribute( u"name"_s, u"altitude"_s );
639 altitudeVendorOptionElem.appendChild( doc.createTextNode( QString::number( altitude() ) ) );
640 shadedReliefElem.appendChild( altitudeVendorOptionElem );
641
642 // azimuth VendorOption tag
643 QDomElement azimutVendorOptionElem = doc.createElement( u"sld:VendorOption"_s );
644 azimutVendorOptionElem.setAttribute( u"name"_s, u"azimuth"_s );
645 azimutVendorOptionElem.appendChild( doc.createTextNode( QString::number( azimuth() ) ) );
646 shadedReliefElem.appendChild( azimutVendorOptionElem );
647
648 // multidirectional VendorOption tag
649 QDomElement multidirectionalVendorOptionElem = doc.createElement( u"sld:VendorOption"_s );
650 multidirectionalVendorOptionElem.setAttribute( u"name"_s, u"multidirectional"_s );
651 multidirectionalVendorOptionElem.appendChild( doc.createTextNode( QString::number( multiDirectional() ) ) );
652 shadedReliefElem.appendChild( multidirectionalVendorOptionElem );
653
654 return true;
655}
QFlags< RasterRendererFlag > RasterRendererFlags
Flags which control behavior of raster renderers.
Definition qgis.h:1632
@ Critical
Critical/error message.
Definition qgis.h:163
@ InternalLayerOpacityHandling
The renderer internally handles the raster layer's opacity, so the default layer level opacity handli...
Definition qgis.h:1622
@ Float32
Thirty two bit floating point (float).
Definition qgis.h:401
@ Int16
Sixteen bit signed integer (qint16).
Definition qgis.h:398
@ ARGB32_Premultiplied
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
Definition qgis.h:408
@ UInt16
Sixteen bit unsigned integer (quint16).
Definition qgis.h:397
@ Byte
Eight bit unsigned integer (quint8).
Definition qgis.h:395
@ Int32
Thirty two bit signed integer (qint32).
Definition qgis.h:400
@ UInt32
Thirty two bit unsigned integer (quint32).
Definition qgis.h:399
Defines a QGIS exception class.
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:56
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition qgsfeedback.h:65
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 const QgsSettingsEntryBool * settingsLogCanvasRefreshEvent
Settings entry log canvas refresh event.
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(), Qgis::StringFormat format=Qgis::StringFormat::PlainText)
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)
int dataTypeSize(int bandNo) const
Returns the size (in bytes) for the data type for the specified band.
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.
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:7633
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:7108
#define QgsDebugError(str)
Definition qgslogger.h:59