QGIS API Documentation  3.24.2-Tisler (13c1a02865)
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 }
@ Float32
Thirty two bit floating point (float)
@ Int16
Sixteen bit signed integer (qint16)
@ ARGB32_Premultiplied
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
@ UInt16
Sixteen bit unsigned integer (quint16)
@ Byte
Eight bit unsigned integer (quint8)
@ Int32
Thirty two bit signed integer (qint32)
@ UInt32
Thirty two bit unsigned integer (quint32)
Defines a QGIS exception class.
Definition: qgsexception.h:35
bool isCanceled() const SIP_HOLDGIL
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition: qgsfeedback.h:63
A renderer for generating live hillshade models.
static QgsRasterRenderer * create(const QDomElement &elem, QgsRasterInterface *input)
Factory method to create a new renderer.
void writeXml(QDomDocument &doc, QDomElement &parentElem) const override
Write base class members to xml.
QgsHillshadeRenderer(QgsRasterInterface *input, int band, double lightAzimuth, double lightAltitude)
A renderer for generating live hillshade models.
bool multiDirectional() const
Returns true if the renderer is using multi-directional hillshading.
void setZFactor(double zfactor)
Set the Z scaling factor of the result image.
double azimuth() const
Returns the direction of the light over the raster between 0-360.
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.
int band() const
Returns the band used by the renderer.
void setBand(int bandNo)
Sets the band used by the renderer.
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)
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 ...
static QLatin1String LOGMESSAGE_TAG
OpenCL string for message logs.
Feedback object tailored for raster block reading.
Raster data container.
Base class for processing filters like renderers, reprojector, resampler etc.
virtual QgsRasterBlock * block(int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback *feedback=nullptr)=0
Read block of data using given extent and size.
virtual QgsRasterInterface * input() const
Current input.
virtual int bandCount() const =0
Gets number of bands.
QgsRasterInterface * mInput
virtual QgsRectangle extent() const
Gets the extent of the interface.
Raster renderer pipe that applies colors to a raster.
double mOpacity
Global alpha value (0-1)
int mAlphaBand
Read alpha value from band.
QRgb renderColorForNodataPixel() const
Returns the color for the renderer to use to represent nodata pixels.
void _writeXml(QDomDocument &doc, QDomElement &rasterRendererElem) const
Write upper class info into rasterrenderer element (called by writeXml method of subclasses)
QgsRasterTransparency * mRasterTransparency
Raster transparency per color or value. Overwrites global alpha value.
void copyCommonProperties(const QgsRasterRenderer *other, bool copyMinMaxOrigin=true)
Copies common properties like opacity / transparency data from other renderer.
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.
void readXml(const QDomElement &rendererElem) override
Sets base class members from xml. Usually called from create() methods of subclasses.
int alphaValue(double value, int globalTransparency=255) const
Returns the transparency value for a single value pixel.
bool isEmpty() const
True if there are no entries in the pixel lists except the nodata value.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
Definition: qgsrectangle.h:230
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
Definition: qgsrectangle.h:223
This class is a composition of two QSettings instances:
Definition: qgssettings.h:62
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
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:1578
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
const QString & typeName