31 #include "qgssettings.h"
41 , mLightAngle( lightAngle )
42 , mLightAzimuth( lightAzimuth )
43 , mMultiDirectional( false )
65 int band = elem.attribute( QStringLiteral(
"band" ), QStringLiteral(
"0" ) ).toInt();
66 double azimuth = elem.attribute( QStringLiteral(
"azimuth" ), QStringLiteral(
"315" ) ).toDouble();
67 double angle = elem.attribute( QStringLiteral(
"angle" ), QStringLiteral(
"45" ) ).toDouble();
68 double zFactor = elem.attribute( QStringLiteral(
"zfactor" ), QStringLiteral(
"1" ) ).toDouble();
69 bool multiDirectional = elem.attribute( QStringLiteral(
"multidirection" ), QStringLiteral(
"0" ) ).toInt();
80 if ( parentElem.isNull() )
85 QDomElement rasterRendererElem = doc.createElement( QStringLiteral(
"rasterrenderer" ) );
88 rasterRendererElem.setAttribute( QStringLiteral(
"band" ), mBand );
89 rasterRendererElem.setAttribute( QStringLiteral(
"azimuth" ), QString::number( mLightAzimuth ) );
90 rasterRendererElem.setAttribute( QStringLiteral(
"angle" ), QString::number( mLightAngle ) );
91 rasterRendererElem.setAttribute( QStringLiteral(
"zfactor" ), QString::number( mZFactor ) );
92 rasterRendererElem.setAttribute( QStringLiteral(
"multidirection" ), QString::number( mMultiDirectional ) );
93 parentElem.appendChild( rasterRendererElem );
99 std::unique_ptr< QgsRasterBlock > outputBlock(
new QgsRasterBlock() );
102 QgsDebugMsg( QStringLiteral(
"No input raster!" ) );
103 return outputBlock.release();
106 std::shared_ptr< QgsRasterBlock > inputBlock(
mInput->
block( mBand,
extent, width, height, feedback ) );
108 if ( !inputBlock || inputBlock->isEmpty() )
110 QgsDebugMsg( QStringLiteral(
"No raster data!" ) );
111 return outputBlock.release();
114 std::shared_ptr< QgsRasterBlock > alphaBlock;
119 if ( !alphaBlock || alphaBlock->isEmpty() )
122 return outputBlock.release();
127 alphaBlock = inputBlock;
132 return outputBlock.release();
138 float cellXSize =
static_cast<float>(
extent.
width() ) / width;
139 float cellYSize =
static_cast<float>(
extent.
height() ) / height;
140 float zenithRad =
static_cast<float>( std::max( 0.0, 90 - mLightAngle ) * M_PI / 180.0 );
141 float azimuthRad =
static_cast<float>( -1 * mLightAzimuth * M_PI / 180.0 );
142 float cosZenithRad = std::cos( zenithRad );
143 float sinZenithRad = std::sin( zenithRad );
146 float cos_alt_mul_z = cosZenithRad *
static_cast<float>( mZFactor );
147 float cos_az_mul_cos_alt_mul_z = std::cos( azimuthRad ) * cos_alt_mul_z;
148 float sin_az_mul_cos_alt_mul_z = std::sin( azimuthRad ) * cos_alt_mul_z;
149 float cos_az_mul_cos_alt_mul_z_mul_254 = 254.0f * cos_az_mul_cos_alt_mul_z;
150 float sin_az_mul_cos_alt_mul_z_mul_254 = 254.0f * sin_az_mul_cos_alt_mul_z;
151 float square_z =
static_cast<float>( mZFactor * mZFactor );
152 float sin_altRadians_mul_254 = 254.0f * sinZenithRad;
155 float sin_altRadians_mul_127 = 127.0f * sinZenithRad;
157 float cos225_az_mul_cos_alt_mul_z_mul_127 = -32.87001872802012f * cos_alt_mul_z;
158 float cos_alt_mul_z_mul_127 = 127.0f * cos_alt_mul_z;
169 && inputBlock->dataTypeSize() <= 4 );
175 if ( source.isEmpty() )
183 std::chrono::time_point<std::chrono::system_clock> startTime( std::chrono::system_clock::now() );
191 std::size_t inputDataTypeSize = inputBlock->dataTypeSize();
192 std::size_t outputDataTypeSize = outputBlock->dataTypeSize();
195 switch ( inputBlock->dataType() )
198 typeName = QStringLiteral(
"unsigned char" );
201 typeName = QStringLiteral(
"unsigned int" );
204 typeName = QStringLiteral(
"short" );
207 typeName = QStringLiteral(
"unsigned int" );
213 typeName = QStringLiteral(
"float" );
216 throw QgsException( QStringLiteral(
"Unsupported data type for OpenCL processing." ) );
221 source.replace( QLatin1String(
"__global float *scanLine" ), QStringLiteral(
"__global %1 *scanLine" ).arg(
typeName ) );
225 std::size_t scanLineWidth( inputBlock->width() + 2 );
226 std::size_t inputSize( inputDataTypeSize * inputBlock->width() );
229 std::size_t bufferWidth( width + 2 );
230 std::size_t bufferSize( inputDataTypeSize * bufferWidth );
235 std::unique_ptr<QgsRasterBlock> scanLine = std::make_unique<QgsRasterBlock>( inputBlock->dataType(), scanLineWidth, 1 );
242 std::vector<float> rasterParams;
243 rasterParams.push_back( inputBlock->noDataValue() );
244 rasterParams.push_back( outputBlock->noDataValue() );
245 rasterParams.push_back( mZFactor );
246 rasterParams.push_back( cellXSize );
247 rasterParams.push_back( cellYSize );
248 rasterParams.push_back(
static_cast<float>(
mOpacity ) );
251 rasterParams.push_back( cos_az_mul_cos_alt_mul_z_mul_254 );
252 rasterParams.push_back( sin_az_mul_cos_alt_mul_z_mul_254 );
253 rasterParams.push_back( square_z );
254 rasterParams.push_back( sin_altRadians_mul_254 );
257 rasterParams.push_back( sin_altRadians_mul_127 );
258 rasterParams.push_back( cos225_az_mul_cos_alt_mul_z_mul_127 );
259 rasterParams.push_back( cos_alt_mul_z_mul_127 );
262 rasterParams.push_back(
static_cast<float>( qBlue( defaultNodataColor ) ) );
263 rasterParams.push_back(
static_cast<float>( qGreen( defaultNodataColor ) ) );
264 rasterParams.push_back(
static_cast<float>( qRed( defaultNodataColor ) ) );
265 rasterParams.push_back(
static_cast<float>( qAlpha( defaultNodataColor ) ) / 255.0f );
268 rasterParams.push_back(
static_cast<float>( mMultiDirectional ) );
270 cl::Buffer rasterParamsBuffer( queue, rasterParams.begin(), rasterParams.end(),
true,
false,
nullptr );
271 cl::Buffer scanLine1Buffer( ctx, CL_MEM_READ_ONLY, bufferSize,
nullptr,
nullptr );
272 cl::Buffer scanLine2Buffer( ctx, CL_MEM_READ_ONLY, bufferSize,
nullptr,
nullptr );
273 cl::Buffer scanLine3Buffer( ctx, CL_MEM_READ_ONLY, bufferSize,
nullptr,
nullptr );
274 cl::Buffer *scanLineBuffer[3] = {&scanLine1Buffer, &scanLine2Buffer, &scanLine3Buffer};
276 cl::Buffer resultLineBuffer( ctx, CL_MEM_WRITE_ONLY, outputDataTypeSize * width,
nullptr,
nullptr );
278 static std::map<Qgis::DataType, cl::Program> programCache;
279 cl::Program program = programCache[inputBlock->dataType()];
280 if ( ! program.get() )
284 program = programCache[inputBlock->dataType()];
291 auto kernel = cl::KernelFunctor <
297 > ( program,
"processNineCellWindow" );
301 std::vector<int> rowIndex = {0, 1, 2};
303 for (
int i = 0; i < height; i++ )
312 feedback->
setProgress( 100.0 *
static_cast< double >( i ) / height );
318 scanLine->resetNoDataValue();
319 queue.enqueueWriteBuffer( scanLine1Buffer, CL_TRUE, 0, bufferSize, scanLine->bits( ) );
321 memcpy( scanLine->bits( 0, 1 ), inputBlock->bits( i, 0 ), inputSize );
322 queue.enqueueWriteBuffer( scanLine2Buffer, CL_TRUE, 0, bufferSize, scanLine->bits( ) );
324 memcpy( scanLine->bits( 0, 1 ), inputBlock->bits( i + 1, 0 ), inputSize );
325 queue.enqueueWriteBuffer( scanLine3Buffer, CL_TRUE, 0, bufferSize, scanLine->bits( ) );
331 if ( i == inputBlock->height() - 1 )
333 scanLine->resetNoDataValue();
334 queue.enqueueWriteBuffer( *scanLineBuffer[rowIndex[2]], CL_TRUE, 0, bufferSize, scanLine->bits( ) );
338 queue.enqueueWriteBuffer( *scanLineBuffer[rowIndex[2]], CL_TRUE, inputDataTypeSize * 1 , inputSize, inputBlock->bits( i + 1, 0 ) );
342 kernel( cl::EnqueueArgs(
346 *scanLineBuffer[rowIndex[0]],
347 *scanLineBuffer[rowIndex[1]],
348 *scanLineBuffer[rowIndex[2]],
353 queue.enqueueReadBuffer( resultLineBuffer, CL_TRUE, 0, outputDataTypeSize * outputBlock->width( ), outputBlock->bits( i, 0 ) );
354 std::rotate( rowIndex.begin(), rowIndex.begin() + 1, rowIndex.end() );
357 catch ( cl::Error &e )
372 for (
qgssize i = 0; i < static_cast<qgssize>( height ); i++ )
375 for (
qgssize j = 0; j < static_cast<qgssize>( width ); j++ )
378 if ( inputBlock->isNoData( i, j ) )
380 outputBlock->setColor(
static_cast<int>( i ),
static_cast<int>( j ), defaultNodataColor );
384 qgssize iUp, iDown, jLeft, jRight;
390 else if ( i <
static_cast<qgssize>( height ) - 1 )
406 else if ( j <
static_cast<qgssize>( width ) - 1 )
428 x22 = inputBlock->value( i, j );
430 x11 = inputBlock->isNoData( iUp, jLeft ) ? x22 : inputBlock->value( iUp, jLeft );
431 x21 = inputBlock->isNoData( i, jLeft ) ? x22 : inputBlock->value( i, jLeft );
432 x31 = inputBlock->isNoData( iDown, jLeft ) ? x22 : inputBlock->value( iDown, jLeft );
434 x12 = inputBlock->isNoData( iUp, j ) ? x22 : inputBlock->value( iUp, j );
436 x32 = inputBlock->isNoData( iDown, j ) ? x22 : inputBlock->value( iDown, j );
438 x13 = inputBlock->isNoData( iUp, jRight ) ? x22 : inputBlock->value( iUp, jRight );
439 x23 = inputBlock->isNoData( i, jRight ) ? x22 : inputBlock->value( i, jRight );
440 x33 = inputBlock->isNoData( iDown, jRight ) ? x22 : inputBlock->value( iDown, jRight );
442 double derX = calcFirstDerX( x11, x21, x31, x12, x22, x32, x13, x23, x33, cellXSize );
443 double derY = calcFirstDerY( x11, x21, x31, x12, x22, x32, x13, x23, x33, cellYSize );
448 if ( !mMultiDirectional )
451 grayValue = std::clamp( ( sin_altRadians_mul_254 -
452 ( derY * cos_az_mul_cos_alt_mul_z_mul_254 -
453 derX * sin_az_mul_cos_alt_mul_z_mul_254 ) ) /
454 std::sqrt( 1 + square_z * ( derX * derX + derY * derY ) ),
461 const float xx = derX * derX;
462 const float yy = derY * derY;
463 const float xx_plus_yy = xx + yy;
465 if ( xx_plus_yy == 0.0 )
467 grayValue = std::clamp(
static_cast<float>( 1.0 + sin_altRadians_mul_254 ), 0.0f, 255.0f );
472 float val225_mul_127 = sin_altRadians_mul_127 +
473 ( derX - derY ) * cos225_az_mul_cos_alt_mul_z_mul_127;
474 val225_mul_127 = ( val225_mul_127 <= 0.0 ) ? 0.0 : val225_mul_127;
475 float val270_mul_127 = sin_altRadians_mul_127 -
476 derX * cos_alt_mul_z_mul_127;
477 val270_mul_127 = ( val270_mul_127 <= 0.0 ) ? 0.0 : val270_mul_127;
478 float val315_mul_127 = sin_altRadians_mul_127 +
479 ( derX + derY ) * cos225_az_mul_cos_alt_mul_z_mul_127;
480 val315_mul_127 = ( val315_mul_127 <= 0.0 ) ? 0.0 : val315_mul_127;
481 float val360_mul_127 = sin_altRadians_mul_127 -
482 derY * cos_alt_mul_z_mul_127;
483 val360_mul_127 = ( val360_mul_127 <= 0.0 ) ? 0.0 : val360_mul_127;
486 const float weight_225 = 0.5 * xx_plus_yy - derX * derY;
487 const float weight_270 = xx;
488 const float weight_315 = xx_plus_yy - weight_225;
489 const float weight_360 = yy;
490 const float cang_mul_127 = (
491 ( weight_225 * val225_mul_127 +
492 weight_270 * val270_mul_127 +
493 weight_315 * val315_mul_127 +
494 weight_360 * val360_mul_127 ) / xx_plus_yy ) /
495 ( 1 + square_z * xx_plus_yy );
497 grayValue = std::clamp( 1.0f + cang_mul_127, 0.0f, 255.0f );
508 currentAlpha *= alphaBlock->value( i ) / 255.0;
513 outputBlock->setColor( i, j, qRgba( grayValue, grayValue, grayValue, 255 ) );
517 outputBlock->setColor( i, j, qRgba( currentAlpha * grayValue, currentAlpha * grayValue, currentAlpha * grayValue, currentAlpha * 255 ) );
526 if ( QgsSettings().value( QStringLiteral(
"Map/logCanvasRefreshEvent" ),
false ).toBool() )
529 .arg( useOpenCL ? QStringLiteral(
"OpenCL" ) : QStringLiteral(
"CPU" ) )
532 .arg( std::chrono::duration_cast<std::chrono::milliseconds>( std::chrono::system_clock::now() - startTime ).count() ),
538 return outputBlock.release();
561 double QgsHillshadeRenderer::calcFirstDerX(
double x11,
double x21,
double x31,
double x12,
double x22,
double x32,
double x13,
double x23,
double x33,
double cellsize )
566 return ( ( x13 + x23 + x23 + x33 ) - ( x11 + x21 + x21 + x31 ) ) / ( 8 * cellsize );
569 double QgsHillshadeRenderer::calcFirstDerY(
double x11,
double x21,
double x31,
double x12,
double x22,
double x32,
double x13,
double x23,
double x33,
double cellsize )
574 return ( ( x31 + x32 + x32 + x33 ) - ( x11 + x12 + x12 + x13 ) ) / ( 8 * -cellsize );
583 QDomNodeList elements = element.elementsByTagName( QStringLiteral(
"sld:RasterSymbolizer" ) );
584 if ( elements.size() == 0 )
588 QDomElement rasterSymbolizerElem = elements.at( 0 ).toElement();
595 QDomElement channelSelectionElem = doc.createElement( QStringLiteral(
"sld:ChannelSelection" ) );
596 elements = rasterSymbolizerElem.elementsByTagName( QStringLiteral(
"sld:Opacity" ) );
597 if ( elements.size() != 0 )
599 rasterSymbolizerElem.insertAfter( channelSelectionElem, elements.at( 0 ) );
603 elements = rasterSymbolizerElem.elementsByTagName( QStringLiteral(
"sld:Geometry" ) );
604 if ( elements.size() != 0 )
606 rasterSymbolizerElem.insertAfter( channelSelectionElem, elements.at( 0 ) );
610 rasterSymbolizerElem.insertBefore( channelSelectionElem, rasterSymbolizerElem.firstChild() );
615 QDomElement channelElem = doc.createElement( QStringLiteral(
"sld:GrayChannel" ) );
616 channelSelectionElem.appendChild( channelElem );
619 QDomElement sourceChannelNameElem = doc.createElement( QStringLiteral(
"sld:SourceChannelName" ) );
620 sourceChannelNameElem.appendChild( doc.createTextNode( QString::number(
band() ) ) );
621 channelElem.appendChild( sourceChannelNameElem );
625 QDomElement shadedReliefElem = doc.createElement( QStringLiteral(
"sld:ShadedRelief" ) );
626 rasterSymbolizerElem.appendChild( shadedReliefElem );
629 QDomElement brightnessOnlyElem = doc.createElement( QStringLiteral(
"sld:BrightnessOnly" ) );
630 brightnessOnlyElem.appendChild( doc.createTextNode( QStringLiteral(
"true" ) ) );
631 shadedReliefElem.appendChild( brightnessOnlyElem );
634 QDomElement reliefFactorElem = doc.createElement( QStringLiteral(
"sld:ReliefFactor" ) );
635 reliefFactorElem.appendChild( doc.createTextNode( QString::number(
zFactor() ) ) );
636 shadedReliefElem.appendChild( reliefFactorElem );
639 QDomElement altitudeVendorOptionElem = doc.createElement( QStringLiteral(
"sld:VendorOption" ) );
640 altitudeVendorOptionElem.setAttribute( QStringLiteral(
"name" ), QStringLiteral(
"altitude" ) );
641 altitudeVendorOptionElem.appendChild( doc.createTextNode( QString::number(
altitude() ) ) );
642 shadedReliefElem.appendChild( altitudeVendorOptionElem );
645 QDomElement azimutVendorOptionElem = doc.createElement( QStringLiteral(
"sld:VendorOption" ) );
646 azimutVendorOptionElem.setAttribute( QStringLiteral(
"name" ), QStringLiteral(
"azimuth" ) );
647 azimutVendorOptionElem.appendChild( doc.createTextNode( QString::number(
azimuth() ) ) );
648 shadedReliefElem.appendChild( azimutVendorOptionElem );
651 QDomElement multidirectionalVendorOptionElem = doc.createElement( QStringLiteral(
"sld:VendorOption" ) );
652 multidirectionalVendorOptionElem.setAttribute( QStringLiteral(
"name" ), QStringLiteral(
"multidirectional" ) );
653 multidirectionalVendorOptionElem.appendChild( doc.createTextNode( QString::number(
multiDirectional() ) ) );
654 shadedReliefElem.appendChild( multidirectionalVendorOptionElem );
@ 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.
bool isCanceled() const SIP_HOLDGIL
Tells whether the operation has been canceled already.
void setProgress(double progress)
Sets the current progress for the feedback object.
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.
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.
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
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)
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...
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)