40 , mLightAngle( lightAngle )
41 , mLightAzimuth( lightAzimuth )
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();
78 if ( parentElem.isNull() )
83 QDomElement rasterRendererElem = doc.createElement( QStringLiteral(
"rasterrenderer" ) );
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 );
97 std::unique_ptr< QgsRasterBlock > outputBlock(
new QgsRasterBlock() );
100 QgsDebugMsg( QStringLiteral(
"No input raster!" ) );
101 return outputBlock.release();
104 std::shared_ptr< QgsRasterBlock > inputBlock(
mInput->
block( mBand,
extent, width, height, feedback ) );
106 if ( !inputBlock || inputBlock->isEmpty() )
108 QgsDebugMsg( QStringLiteral(
"No raster data!" ) );
109 return outputBlock.release();
112 std::shared_ptr< QgsRasterBlock > alphaBlock;
117 if ( !alphaBlock || alphaBlock->isEmpty() )
120 return outputBlock.release();
125 alphaBlock = inputBlock;
130 return outputBlock.release();
133 if ( width == 0 || height == 0 )
134 return outputBlock.release();
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 );
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;
156 float sin_altRadians_mul_127 = 127.0f * sinZenithRad;
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;
170 && inputBlock->dataTypeSize() <= 4 );
176 if ( source.isEmpty() )
184 std::chrono::time_point<std::chrono::system_clock> startTime( std::chrono::system_clock::now() );
192 std::size_t inputDataTypeSize = inputBlock->dataTypeSize();
193 std::size_t outputDataTypeSize = outputBlock->dataTypeSize();
196 switch ( inputBlock->dataType() )
199 typeName = QStringLiteral(
"unsigned char" );
202 typeName = QStringLiteral(
"unsigned int" );
205 typeName = QStringLiteral(
"short" );
208 typeName = QStringLiteral(
"unsigned int" );
214 typeName = QStringLiteral(
"float" );
217 throw QgsException( QStringLiteral(
"Unsupported data type for OpenCL processing." ) );
222 source.replace( QLatin1String(
"__global float *scanLine" ), QStringLiteral(
"__global %1 *scanLine" ).arg(
typeName ) );
226 std::size_t scanLineWidth( inputBlock->width() + 2 );
227 std::size_t inputSize( inputDataTypeSize * inputBlock->width() );
230 std::size_t bufferWidth( width + 2 );
231 std::size_t bufferSize( inputDataTypeSize * bufferWidth );
236 std::unique_ptr<QgsRasterBlock> scanLine = std::make_unique<QgsRasterBlock>( inputBlock->dataType(), scanLineWidth, 1 );
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 ) );
252 rasterParams.push_back( cos_az_mul_cos_alt_mul_z_mul_254 );
253 rasterParams.push_back( sin_az_mul_cos_alt_mul_z_mul_254 );
254 rasterParams.push_back( square_z );
255 rasterParams.push_back( sin_altRadians_mul_254 );
258 rasterParams.push_back( sin_altRadians_mul_127 );
259 rasterParams.push_back( cos225_az_mul_cos_alt_mul_z_mul_127 );
260 rasterParams.push_back( cos_alt_mul_z_mul_127 );
263 rasterParams.push_back(
static_cast<float>( qBlue( defaultNodataColor ) ) );
264 rasterParams.push_back(
static_cast<float>( qGreen( defaultNodataColor ) ) );
265 rasterParams.push_back(
static_cast<float>( qRed( defaultNodataColor ) ) );
266 rasterParams.push_back(
static_cast<float>( qAlpha( defaultNodataColor ) ) / 255.0f );
269 rasterParams.push_back(
static_cast<float>( mMultiDirectional ) );
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};
277 cl::Buffer resultLineBuffer( ctx, CL_MEM_WRITE_ONLY, outputDataTypeSize * width,
nullptr,
nullptr );
279 static std::map<Qgis::DataType, cl::Program> programCache;
280 cl::Program program = programCache[inputBlock->dataType()];
281 if ( ! program.get() )
285 program = programCache[inputBlock->dataType()];
292 auto kernel = cl::KernelFunctor <
298 > ( program,
"processNineCellWindow" );
302 std::vector<int> rowIndex = {0, 1, 2};
304 for (
int i = 0; i < height; i++ )
313 feedback->
setProgress( 100.0 *
static_cast< double >( i ) / height );
319 scanLine->resetNoDataValue();
320 queue.enqueueWriteBuffer( scanLine1Buffer, CL_TRUE, 0, bufferSize, scanLine->bits( ) );
322 memcpy( scanLine->bits( 0, 1 ), inputBlock->bits( i, 0 ), inputSize );
323 queue.enqueueWriteBuffer( scanLine2Buffer, CL_TRUE, 0, bufferSize, scanLine->bits( ) );
325 memcpy( scanLine->bits( 0, 1 ), inputBlock->bits( i + 1, 0 ), inputSize );
326 queue.enqueueWriteBuffer( scanLine3Buffer, CL_TRUE, 0, bufferSize, scanLine->bits( ) );
332 if ( i == inputBlock->height() - 1 )
334 scanLine->resetNoDataValue();
335 queue.enqueueWriteBuffer( *scanLineBuffer[rowIndex[2]], CL_TRUE, 0, bufferSize, scanLine->bits( ) );
339 queue.enqueueWriteBuffer( *scanLineBuffer[rowIndex[2]], CL_TRUE, inputDataTypeSize * 1 , inputSize, inputBlock->bits( i + 1, 0 ) );
343 kernel( cl::EnqueueArgs(
347 *scanLineBuffer[rowIndex[0]],
348 *scanLineBuffer[rowIndex[1]],
349 *scanLineBuffer[rowIndex[2]],
354 queue.enqueueReadBuffer( resultLineBuffer, CL_TRUE, 0, outputDataTypeSize * outputBlock->width( ), outputBlock->bits( i, 0 ) );
355 std::rotate( rowIndex.begin(), rowIndex.begin() + 1, rowIndex.end() );
358 catch ( cl::Error &e )
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};
376 for (
int row = 0; row < height; row++ )
378 for (
int col = 0; col < width; col++ )
386 else if ( row == height - 1 )
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];
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];
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];
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];
430 if ( col < width - 1 )
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] );
439 outputBlock->setColor( row, col, defaultNodataColor );
444 const double x22 = pixelValues[4];
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];
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];
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 );
463 if ( !mMultiDirectional )
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 ) ),
476 const float xx = derX * derX;
477 const float yy = derY * derY;
478 const float xx_plus_yy = xx + yy;
480 if ( xx_plus_yy == 0.0 )
482 grayValue = std::clamp(
static_cast<float>( 1.0 + sin_altRadians_mul_254 ), 0.0f, 255.0f );
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;
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 );
512 grayValue = std::clamp( 1.0f + cang_mul_127, 0.0f, 255.0f );
523 currentAlpha *= alphaBlock->value( row ) / 255.0;
528 outputBlock->setColor( row, col, qRgba( grayValue, grayValue, grayValue, 255 ) );
532 outputBlock->setColor( row, col, qRgba( currentAlpha * grayValue, currentAlpha * grayValue, currentAlpha * grayValue, currentAlpha * 255 ) );
541 if (
QgsSettings().value( QStringLiteral(
"Map/logCanvasRefreshEvent" ),
false ).toBool() )
544 .arg( useOpenCL ? QStringLiteral(
"OpenCL" ) : QStringLiteral(
"CPU" ) )
547 .arg( std::chrono::duration_cast<std::chrono::milliseconds>( std::chrono::system_clock::now() - startTime ).count() ),
553 return outputBlock.release();
582 QDomNodeList elements = element.elementsByTagName( QStringLiteral(
"sld:RasterSymbolizer" ) );
583 if ( elements.size() == 0 )
587 QDomElement rasterSymbolizerElem = elements.at( 0 ).toElement();
594 QDomElement channelSelectionElem = doc.createElement( QStringLiteral(
"sld:ChannelSelection" ) );
595 elements = rasterSymbolizerElem.elementsByTagName( QStringLiteral(
"sld:Opacity" ) );
596 if ( elements.size() != 0 )
598 rasterSymbolizerElem.insertAfter( channelSelectionElem, elements.at( 0 ) );
602 elements = rasterSymbolizerElem.elementsByTagName( QStringLiteral(
"sld:Geometry" ) );
603 if ( elements.size() != 0 )
605 rasterSymbolizerElem.insertAfter( channelSelectionElem, elements.at( 0 ) );
609 rasterSymbolizerElem.insertBefore( channelSelectionElem, rasterSymbolizerElem.firstChild() );
614 QDomElement channelElem = doc.createElement( QStringLiteral(
"sld:GrayChannel" ) );
615 channelSelectionElem.appendChild( channelElem );
618 QDomElement sourceChannelNameElem = doc.createElement( QStringLiteral(
"sld:SourceChannelName" ) );
619 sourceChannelNameElem.appendChild( doc.createTextNode( QString::number(
band() ) ) );
620 channelElem.appendChild( sourceChannelNameElem );
624 QDomElement shadedReliefElem = doc.createElement( QStringLiteral(
"sld:ShadedRelief" ) );
625 rasterSymbolizerElem.appendChild( shadedReliefElem );
628 QDomElement brightnessOnlyElem = doc.createElement( QStringLiteral(
"sld:BrightnessOnly" ) );
629 brightnessOnlyElem.appendChild( doc.createTextNode( QStringLiteral(
"true" ) ) );
630 shadedReliefElem.appendChild( brightnessOnlyElem );
633 QDomElement reliefFactorElem = doc.createElement( QStringLiteral(
"sld:ReliefFactor" ) );
634 reliefFactorElem.appendChild( doc.createTextNode( QString::number(
zFactor() ) ) );
635 shadedReliefElem.appendChild( reliefFactorElem );
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 );
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 );
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 );
@ 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.
This class is a composition of two QSettings instances:
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)
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)