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