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