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