40 , mLightAngle( lightAngle )
41 , mLightAzimuth( lightAzimuth )
68 int band = elem.attribute( QStringLiteral(
"band" ), QStringLiteral(
"0" ) ).toInt();
69 double azimuth = elem.attribute( QStringLiteral(
"azimuth" ), QStringLiteral(
"315" ) ).toDouble();
70 double angle = elem.attribute( QStringLiteral(
"angle" ), QStringLiteral(
"45" ) ).toDouble();
71 double zFactor = elem.attribute( QStringLiteral(
"zfactor" ), QStringLiteral(
"1" ) ).toDouble();
72 bool multiDirectional = elem.attribute( QStringLiteral(
"multidirection" ), QStringLiteral(
"0" ) ).toInt();
83 if ( parentElem.isNull() )
88 QDomElement rasterRendererElem = doc.createElement( QStringLiteral(
"rasterrenderer" ) );
91 rasterRendererElem.setAttribute( QStringLiteral(
"band" ), mBand );
92 rasterRendererElem.setAttribute( QStringLiteral(
"azimuth" ), QString::number( mLightAzimuth ) );
93 rasterRendererElem.setAttribute( QStringLiteral(
"angle" ), QString::number( mLightAngle ) );
94 rasterRendererElem.setAttribute( QStringLiteral(
"zfactor" ), QString::number( mZFactor ) );
95 rasterRendererElem.setAttribute( QStringLiteral(
"multidirection" ), QString::number( mMultiDirectional ) );
96 parentElem.appendChild( rasterRendererElem );
102 std::unique_ptr< QgsRasterBlock > outputBlock(
new QgsRasterBlock() );
105 QgsDebugMsg( QStringLiteral(
"No input raster!" ) );
106 return outputBlock.release();
109 std::shared_ptr< QgsRasterBlock > inputBlock(
mInput->
block( mBand,
extent, width, height, feedback ) );
111 if ( !inputBlock || inputBlock->isEmpty() )
113 QgsDebugMsg( QStringLiteral(
"No raster data!" ) );
114 return outputBlock.release();
117 std::shared_ptr< QgsRasterBlock > alphaBlock;
122 if ( !alphaBlock || alphaBlock->isEmpty() )
125 return outputBlock.release();
130 alphaBlock = inputBlock;
135 return outputBlock.release();
138 if ( width == 0 || height == 0 )
139 return outputBlock.release();
144 float cellXSize =
static_cast<float>(
extent.
width() ) / width;
145 float cellYSize =
static_cast<float>(
extent.
height() ) / height;
146 float zenithRad =
static_cast<float>( std::max( 0.0, 90 - mLightAngle ) * M_PI / 180.0 );
147 float azimuthRad =
static_cast<float>( -1 * mLightAzimuth * M_PI / 180.0 );
148 float cosZenithRad = std::cos( zenithRad );
149 float sinZenithRad = std::sin( zenithRad );
152 float cos_alt_mul_z = cosZenithRad *
static_cast<float>( mZFactor );
153 float cos_az_mul_cos_alt_mul_z = std::cos( azimuthRad ) * cos_alt_mul_z;
154 float sin_az_mul_cos_alt_mul_z = std::sin( azimuthRad ) * cos_alt_mul_z;
155 float cos_az_mul_cos_alt_mul_z_mul_254 = 254.0f * cos_az_mul_cos_alt_mul_z;
156 float sin_az_mul_cos_alt_mul_z_mul_254 = 254.0f * sin_az_mul_cos_alt_mul_z;
157 float square_z =
static_cast<float>( mZFactor * mZFactor );
158 float sin_altRadians_mul_254 = 254.0f * sinZenithRad;
161 float sin_altRadians_mul_127 = 127.0f * sinZenithRad;
163 float cos225_az_mul_cos_alt_mul_z_mul_127 = -32.87001872802012f * cos_alt_mul_z;
164 float cos_alt_mul_z_mul_127 = 127.0f * cos_alt_mul_z;
175 && inputBlock->dataTypeSize() <= 4 );
181 if ( source.isEmpty() )
189 std::chrono::time_point<std::chrono::system_clock> startTime( std::chrono::system_clock::now() );
197 std::size_t inputDataTypeSize = inputBlock->dataTypeSize();
198 std::size_t outputDataTypeSize = outputBlock->dataTypeSize();
201 switch ( inputBlock->dataType() )
204 typeName = QStringLiteral(
"unsigned char" );
207 typeName = QStringLiteral(
"unsigned int" );
210 typeName = QStringLiteral(
"short" );
213 typeName = QStringLiteral(
"unsigned int" );
219 typeName = QStringLiteral(
"float" );
222 throw QgsException( QStringLiteral(
"Unsupported data type for OpenCL processing." ) );
227 source.replace( QLatin1String(
"__global float *scanLine" ), QStringLiteral(
"__global %1 *scanLine" ).arg(
typeName ) );
231 std::size_t scanLineWidth( inputBlock->width() + 2 );
232 std::size_t inputSize( inputDataTypeSize * inputBlock->width() );
235 std::size_t bufferWidth( width + 2 );
236 std::size_t bufferSize( inputDataTypeSize * bufferWidth );
241 std::unique_ptr<QgsRasterBlock> scanLine = std::make_unique<QgsRasterBlock>( inputBlock->dataType(), scanLineWidth, 1 );
248 std::vector<float> rasterParams;
249 rasterParams.push_back( inputBlock->noDataValue() );
250 rasterParams.push_back( outputBlock->noDataValue() );
251 rasterParams.push_back( mZFactor );
252 rasterParams.push_back( cellXSize );
253 rasterParams.push_back( cellYSize );
254 rasterParams.push_back(
static_cast<float>(
mOpacity ) );
257 rasterParams.push_back( cos_az_mul_cos_alt_mul_z_mul_254 );
258 rasterParams.push_back( sin_az_mul_cos_alt_mul_z_mul_254 );
259 rasterParams.push_back( square_z );
260 rasterParams.push_back( sin_altRadians_mul_254 );
263 rasterParams.push_back( sin_altRadians_mul_127 );
264 rasterParams.push_back( cos225_az_mul_cos_alt_mul_z_mul_127 );
265 rasterParams.push_back( cos_alt_mul_z_mul_127 );
268 rasterParams.push_back(
static_cast<float>( qBlue( defaultNodataColor ) ) );
269 rasterParams.push_back(
static_cast<float>( qGreen( defaultNodataColor ) ) );
270 rasterParams.push_back(
static_cast<float>( qRed( defaultNodataColor ) ) );
271 rasterParams.push_back(
static_cast<float>( qAlpha( defaultNodataColor ) ) / 255.0f );
274 rasterParams.push_back(
static_cast<float>( mMultiDirectional ) );
276 cl::Buffer rasterParamsBuffer( queue, rasterParams.begin(), rasterParams.end(),
true,
false,
nullptr );
277 cl::Buffer scanLine1Buffer( ctx, CL_MEM_READ_ONLY, bufferSize,
nullptr,
nullptr );
278 cl::Buffer scanLine2Buffer( ctx, CL_MEM_READ_ONLY, bufferSize,
nullptr,
nullptr );
279 cl::Buffer scanLine3Buffer( ctx, CL_MEM_READ_ONLY, bufferSize,
nullptr,
nullptr );
280 cl::Buffer *scanLineBuffer[3] = {&scanLine1Buffer, &scanLine2Buffer, &scanLine3Buffer};
282 cl::Buffer resultLineBuffer( ctx, CL_MEM_WRITE_ONLY, outputDataTypeSize * width,
nullptr,
nullptr );
284 static std::map<Qgis::DataType, cl::Program> programCache;
285 cl::Program program = programCache[inputBlock->dataType()];
286 if ( ! program.get() )
290 program = programCache[inputBlock->dataType()];
297 auto kernel = cl::KernelFunctor <
303 > ( program,
"processNineCellWindow" );
307 std::vector<int> rowIndex = {0, 1, 2};
309 for (
int i = 0; i < height; i++ )
318 feedback->
setProgress( 100.0 *
static_cast< double >( i ) / height );
324 scanLine->resetNoDataValue();
325 queue.enqueueWriteBuffer( scanLine1Buffer, CL_TRUE, 0, bufferSize, scanLine->bits( ) );
327 memcpy( scanLine->bits( 0, 1 ), inputBlock->bits( i, 0 ), inputSize );
328 queue.enqueueWriteBuffer( scanLine2Buffer, CL_TRUE, 0, bufferSize, scanLine->bits( ) );
330 memcpy( scanLine->bits( 0, 1 ), inputBlock->bits( i + 1, 0 ), inputSize );
331 queue.enqueueWriteBuffer( scanLine3Buffer, CL_TRUE, 0, bufferSize, scanLine->bits( ) );
337 if ( i == inputBlock->height() - 1 )
339 scanLine->resetNoDataValue();
340 queue.enqueueWriteBuffer( *scanLineBuffer[rowIndex[2]], CL_TRUE, 0, bufferSize, scanLine->bits( ) );
344 queue.enqueueWriteBuffer( *scanLineBuffer[rowIndex[2]], CL_TRUE, inputDataTypeSize * 1 , inputSize, inputBlock->bits( i + 1, 0 ) );
348 kernel( cl::EnqueueArgs(
352 *scanLineBuffer[rowIndex[0]],
353 *scanLineBuffer[rowIndex[1]],
354 *scanLineBuffer[rowIndex[2]],
359 queue.enqueueReadBuffer( resultLineBuffer, CL_TRUE, 0, outputDataTypeSize * outputBlock->width( ), outputBlock->bits( i, 0 ) );
360 std::rotate( rowIndex.begin(), rowIndex.begin() + 1, rowIndex.end() );
363 catch ( cl::Error &e )
378 double pixelValues[9] {0, 0, 0, 0, 0, 0, 0, 0, 0};
379 bool isNoData[9] {
false,
false,
false,
false,
false,
false,
false,
false,
false};
381 for (
int row = 0; row < height; row++ )
383 for (
int col = 0; col < width; col++ )
391 else if ( row == height - 1 )
399 pixelValues[ 0 ] = inputBlock->valueAndNoData( iUp, 0, isNoData[0] );
400 pixelValues[ 1 ] = pixelValues[0];
401 isNoData[1] = isNoData[0];
402 pixelValues[ 2 ] = pixelValues[0];
403 isNoData[2] = isNoData[0];
405 pixelValues[ 3 ] = inputBlock->valueAndNoData( row, 0, isNoData[3] );
406 pixelValues[ 4 ] = pixelValues[3];
407 isNoData[4] = isNoData[3];
408 pixelValues[ 5 ] = pixelValues[3];
409 isNoData[5] = isNoData[3];
411 pixelValues[ 6 ] = inputBlock->valueAndNoData( iDown, 0, isNoData[6] );
412 pixelValues[ 7 ] = pixelValues[6];
413 isNoData[7] = isNoData[6];
414 pixelValues[ 8 ] = pixelValues[6];
415 isNoData[8] = isNoData[6];
420 pixelValues[ 0 ] = pixelValues[1];
421 pixelValues[ 1 ] = pixelValues[2];
422 pixelValues[ 3 ] = pixelValues[4];
423 pixelValues[ 4 ] = pixelValues[5];
424 pixelValues[ 6 ] = pixelValues[7];
425 pixelValues[ 7 ] = pixelValues[8];
426 isNoData[ 0 ] = isNoData[1];
427 isNoData[ 1 ] = isNoData[2];
428 isNoData[ 3 ] = isNoData[4];
429 isNoData[ 4 ] = isNoData[5];
430 isNoData[ 6 ] = isNoData[7];
431 isNoData[ 7 ] = isNoData[8];
435 if ( col < width - 1 )
437 pixelValues[2] = inputBlock->valueAndNoData( iUp, col + 1, isNoData[2] );
438 pixelValues[5] = inputBlock->valueAndNoData( row, col + 1, isNoData[5] );
439 pixelValues[8] = inputBlock->valueAndNoData( iDown, col + 1, isNoData[8] );
444 outputBlock->setColor( row, col, defaultNodataColor );
449 const double x22 = pixelValues[4];
451 const double x11 = isNoData[0] ? x22 : pixelValues[0];
452 const double x21 = isNoData[3] ? x22 : pixelValues[3];
453 const double x31 = isNoData[6] ? x22 : pixelValues[6];
454 const double x12 = isNoData[1] ? x22 : pixelValues[1];
456 const double x32 = isNoData[7] ? x22 : pixelValues[7];
457 const double x13 = isNoData[2] ? x22 : pixelValues[2];
458 const double x23 = isNoData[5] ? x22 : pixelValues[5];
459 const double x33 = isNoData[8] ? x22 : pixelValues[8];
462 const double derX = ( ( x13 + x23 + x23 + x33 ) - ( x11 + x21 + x21 + x31 ) ) / ( 8 * cellXSize );
463 const double derY = ( ( x31 + x32 + x32 + x33 ) - ( x11 + x12 + x12 + x13 ) ) / ( 8 * -cellYSize );
468 if ( !mMultiDirectional )
471 grayValue = std::clamp( ( sin_altRadians_mul_254 -
472 ( derY * cos_az_mul_cos_alt_mul_z_mul_254 -
473 derX * sin_az_mul_cos_alt_mul_z_mul_254 ) ) /
474 std::sqrt( 1 + square_z * ( derX * derX + derY * derY ) ),
481 const float xx = derX * derX;
482 const float yy = derY * derY;
483 const float xx_plus_yy = xx + yy;
485 if ( xx_plus_yy == 0.0 )
487 grayValue = std::clamp(
static_cast<float>( 1.0 + sin_altRadians_mul_254 ), 0.0f, 255.0f );
492 float val225_mul_127 = sin_altRadians_mul_127 +
493 ( derX - derY ) * cos225_az_mul_cos_alt_mul_z_mul_127;
494 val225_mul_127 = ( val225_mul_127 <= 0.0 ) ? 0.0 : val225_mul_127;
495 float val270_mul_127 = sin_altRadians_mul_127 -
496 derX * cos_alt_mul_z_mul_127;
497 val270_mul_127 = ( val270_mul_127 <= 0.0 ) ? 0.0 : val270_mul_127;
498 float val315_mul_127 = sin_altRadians_mul_127 +
499 ( derX + derY ) * cos225_az_mul_cos_alt_mul_z_mul_127;
500 val315_mul_127 = ( val315_mul_127 <= 0.0 ) ? 0.0 : val315_mul_127;
501 float val360_mul_127 = sin_altRadians_mul_127 -
502 derY * cos_alt_mul_z_mul_127;
503 val360_mul_127 = ( val360_mul_127 <= 0.0 ) ? 0.0 : val360_mul_127;
506 const float weight_225 = 0.5 * xx_plus_yy - derX * derY;
507 const float weight_270 = xx;
508 const float weight_315 = xx_plus_yy - weight_225;
509 const float weight_360 = yy;
510 const float cang_mul_127 = (
511 ( weight_225 * val225_mul_127 +
512 weight_270 * val270_mul_127 +
513 weight_315 * val315_mul_127 +
514 weight_360 * val360_mul_127 ) / xx_plus_yy ) /
515 ( 1 + square_z * xx_plus_yy );
517 grayValue = std::clamp( 1.0f + cang_mul_127, 0.0f, 255.0f );
528 currentAlpha *= alphaBlock->value( row ) / 255.0;
533 outputBlock->setColor( row, col, qRgba( grayValue, grayValue, grayValue, 255 ) );
537 outputBlock->setColor( row, col, qRgba( currentAlpha * grayValue, currentAlpha * grayValue, currentAlpha * grayValue, currentAlpha * 255 ) );
546 if (
QgsSettings().value( QStringLiteral(
"Map/logCanvasRefreshEvent" ),
false ).toBool() )
549 .arg( useOpenCL ? QStringLiteral(
"OpenCL" ) : QStringLiteral(
"CPU" ) )
552 .arg( std::chrono::duration_cast<std::chrono::milliseconds>( std::chrono::system_clock::now() - startTime ).count() ),
558 return outputBlock.release();
587 QDomNodeList elements = element.elementsByTagName( QStringLiteral(
"sld:RasterSymbolizer" ) );
588 if ( elements.size() == 0 )
592 QDomElement rasterSymbolizerElem = elements.at( 0 ).toElement();
599 QDomElement channelSelectionElem = doc.createElement( QStringLiteral(
"sld:ChannelSelection" ) );
600 elements = rasterSymbolizerElem.elementsByTagName( QStringLiteral(
"sld:Opacity" ) );
601 if ( elements.size() != 0 )
603 rasterSymbolizerElem.insertAfter( channelSelectionElem, elements.at( 0 ) );
607 elements = rasterSymbolizerElem.elementsByTagName( QStringLiteral(
"sld:Geometry" ) );
608 if ( elements.size() != 0 )
610 rasterSymbolizerElem.insertAfter( channelSelectionElem, elements.at( 0 ) );
614 rasterSymbolizerElem.insertBefore( channelSelectionElem, rasterSymbolizerElem.firstChild() );
619 QDomElement channelElem = doc.createElement( QStringLiteral(
"sld:GrayChannel" ) );
620 channelSelectionElem.appendChild( channelElem );
623 QDomElement sourceChannelNameElem = doc.createElement( QStringLiteral(
"sld:SourceChannelName" ) );
624 sourceChannelNameElem.appendChild( doc.createTextNode( QString::number(
band() ) ) );
625 channelElem.appendChild( sourceChannelNameElem );
629 QDomElement shadedReliefElem = doc.createElement( QStringLiteral(
"sld:ShadedRelief" ) );
630 rasterSymbolizerElem.appendChild( shadedReliefElem );
633 QDomElement brightnessOnlyElem = doc.createElement( QStringLiteral(
"sld:BrightnessOnly" ) );
634 brightnessOnlyElem.appendChild( doc.createTextNode( QStringLiteral(
"true" ) ) );
635 shadedReliefElem.appendChild( brightnessOnlyElem );
638 QDomElement reliefFactorElem = doc.createElement( QStringLiteral(
"sld:ReliefFactor" ) );
639 reliefFactorElem.appendChild( doc.createTextNode( QString::number(
zFactor() ) ) );
640 shadedReliefElem.appendChild( reliefFactorElem );
643 QDomElement altitudeVendorOptionElem = doc.createElement( QStringLiteral(
"sld:VendorOption" ) );
644 altitudeVendorOptionElem.setAttribute( QStringLiteral(
"name" ), QStringLiteral(
"altitude" ) );
645 altitudeVendorOptionElem.appendChild( doc.createTextNode( QString::number(
altitude() ) ) );
646 shadedReliefElem.appendChild( altitudeVendorOptionElem );
649 QDomElement azimutVendorOptionElem = doc.createElement( QStringLiteral(
"sld:VendorOption" ) );
650 azimutVendorOptionElem.setAttribute( QStringLiteral(
"name" ), QStringLiteral(
"azimuth" ) );
651 azimutVendorOptionElem.appendChild( doc.createTextNode( QString::number(
azimuth() ) ) );
652 shadedReliefElem.appendChild( azimutVendorOptionElem );
655 QDomElement multidirectionalVendorOptionElem = doc.createElement( QStringLiteral(
"sld:VendorOption" ) );
656 multidirectionalVendorOptionElem.setAttribute( QStringLiteral(
"name" ), QStringLiteral(
"multidirectional" ) );
657 multidirectionalVendorOptionElem.appendChild( doc.createTextNode( QString::number(
multiDirectional() ) ) );
658 shadedReliefElem.appendChild( multidirectionalVendorOptionElem );
@ InternalLayerOpacityHandling
The renderer internally handles the raster layer's opacity, so the default layer level opacity handli...
@ 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.
Qgis::RasterRendererFlags flags() const override
Returns flags which dictate renderer behavior.
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 int bandCount() const =0
Gets number of bands.
QgsRasterInterface * mInput
virtual QgsRectangle extent() const
Gets the extent of the interface.
virtual QgsRasterInterface * input() const
Current input.
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)