Branch data Line data Source code
1 : : /*************************************************************************** 2 : : qgspointcloudrenderer.h 3 : : -------------------- 4 : : begin : October 2020 5 : : copyright : (C) 2020 by Peter Petrik 6 : : email : zilolv 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 : : #ifndef QGSPOINTCLOUDRENDERER_H 19 : : #define QGSPOINTCLOUDRENDERER_H 20 : : 21 : : #include "qgsrendercontext.h" 22 : : 23 : : #include "qgis_core.h" 24 : : #include "qgis_sip.h" 25 : : #include "qgsvector3d.h" 26 : : #include "qgspointcloudattribute.h" 27 : : 28 : : class QgsPointCloudBlock; 29 : : class QgsLayerTreeLayer; 30 : : class QgsLayerTreeModelLegendNode; 31 : : class QgsPointCloudLayer; 32 : : 33 : : /** 34 : : * \ingroup core 35 : : * \class QgsPointCloudRenderContext 36 : : * 37 : : * \brief Encapsulates the render context for a 2D point cloud rendering operation. 38 : : * 39 : : * \since QGIS 3.18 40 : : */ 41 : 0 : class CORE_EXPORT QgsPointCloudRenderContext 42 : : { 43 : : public: 44 : : 45 : : /** 46 : : * Constructor for QgsPointCloudRenderContext. 47 : : * 48 : : * The \a scale and \a offset arguments specify the scale and offset of the layer's int32 coordinates 49 : : * compared to CRS coordinates respectively. 50 : : * 51 : : * The \a zValueScale argument specifies any constant scaling factor which must be applied to z values 52 : : * taken from the point cloud index. 53 : : * 54 : : * The \a zValueFixedOffset argument specifies any constant offset value which must be added to z values 55 : : * taken from the point cloud index. 56 : : */ 57 : : QgsPointCloudRenderContext( QgsRenderContext &context, const QgsVector3D &scale, const QgsVector3D &offset, 58 : : double zValueScale, double zValueFixedOffset ); 59 : : 60 : : //! QgsPointCloudRenderContext cannot be copied. 61 : : QgsPointCloudRenderContext( const QgsPointCloudRenderContext &rh ) = delete; 62 : : 63 : : //! QgsPointCloudRenderContext cannot be copied. 64 : : QgsPointCloudRenderContext &operator=( const QgsPointCloudRenderContext & ) = delete; 65 : : 66 : : /** 67 : : * Returns a reference to the context's render context. 68 : : */ 69 : 0 : QgsRenderContext &renderContext() { return mRenderContext; } 70 : : 71 : : /** 72 : : * Returns a reference to the context's render context. 73 : : * \note Not available in Python bindings. 74 : : */ 75 : : const QgsRenderContext &renderContext() const { return mRenderContext; } SIP_SKIP 76 : : 77 : : /** 78 : : * Returns the scale of the layer's int32 coordinates compared to CRS coords. 79 : : */ 80 : 0 : QgsVector3D scale() const { return mScale; } 81 : : 82 : : /** 83 : : * Returns the offset of the layer's int32 coordinates compared to CRS coords. 84 : : */ 85 : 0 : QgsVector3D offset() const { return mOffset; } 86 : : 87 : : /** 88 : : * Returns the total number of points rendered. 89 : : */ 90 : : long pointsRendered() const; 91 : : 92 : : /** 93 : : * Increments the count of points rendered by the specified amount. 94 : : * 95 : : * It is a point cloud renderer's responsibility to correctly call this after 96 : : * rendering a block of points. 97 : : */ 98 : : void incrementPointsRendered( long count ); 99 : : 100 : : /** 101 : : * Returns the attributes associated with the rendered block. 102 : : * 103 : : * \see setAttributes() 104 : : */ 105 : : QgsPointCloudAttributeCollection attributes() const { return mAttributes; } 106 : : 107 : : /** 108 : : * Sets the \a attributes associated with the rendered block. 109 : : * 110 : : * \see attributes() 111 : : */ 112 : : void setAttributes( const QgsPointCloudAttributeCollection &attributes ); 113 : : 114 : : /** 115 : : * Returns the size of a single point record. 116 : : */ 117 : 0 : int pointRecordSize() const { return mPointRecordSize; } 118 : : 119 : : /** 120 : : * Returns the offset for the x value in a point record. 121 : : * 122 : : * \see yOffset() 123 : : * \see zOffset() 124 : : */ 125 : 0 : int xOffset() const { return mXOffset; } 126 : : 127 : : /** 128 : : * Returns the offset for the y value in a point record. 129 : : * 130 : : * \see xOffset() 131 : : * \see zOffset() 132 : : */ 133 : 0 : int yOffset() const { return mYOffset; } 134 : : 135 : : /** 136 : : * Returns the offset for the y value in a point record. 137 : : * 138 : : * \see xOffset() 139 : : * \see yOffset() 140 : : */ 141 : 0 : int zOffset() const { return mZOffset; } 142 : : 143 : : /** 144 : : * Returns any constant scaling factor which must be applied to z values taken from the point cloud index. 145 : : * 146 : : * \note Scaling of z values should be applied before the zValueFixedOffset(). 147 : : */ 148 : 0 : double zValueScale() const { return mZValueScale; } 149 : : 150 : : /** 151 : : * Returns any constant offset which must be applied to z values taken from the point cloud index. 152 : : * 153 : : * \note Scaling of z values via zValueScale() should be applied before the zValueFixedOffset(). 154 : : */ 155 : 0 : double zValueFixedOffset() const { return mZValueFixedOffset; } 156 : : 157 : : #ifndef SIP_RUN 158 : : 159 : : /** 160 : : * Retrieves the attribute \a value from \a data at the specified \a offset, where 161 : : * \a type indicates the original data type for the attribute. 162 : : */ 163 : : template <typename T> 164 : 0 : void getAttribute( const char *data, std::size_t offset, QgsPointCloudAttribute::DataType type, T &value ) const 165 : : { 166 : 0 : switch ( type ) 167 : : { 168 : : case QgsPointCloudAttribute::Char: 169 : 0 : value = *( data + offset ); 170 : 0 : return; 171 : : 172 : : case QgsPointCloudAttribute::Int32: 173 : 0 : value = *reinterpret_cast< const qint32 * >( data + offset ); 174 : 0 : return; 175 : : 176 : : case QgsPointCloudAttribute::Short: 177 : 0 : value = *reinterpret_cast< const short * >( data + offset ); 178 : 0 : return; 179 : : 180 : : case QgsPointCloudAttribute::UShort: 181 : 0 : value = *reinterpret_cast< const unsigned short * >( data + offset ); 182 : 0 : return; 183 : : 184 : : case QgsPointCloudAttribute::Float: 185 : 0 : value = *reinterpret_cast< const float * >( data + offset ); 186 : 0 : return; 187 : : 188 : : case QgsPointCloudAttribute::Double: 189 : 0 : value = *reinterpret_cast< const double * >( data + offset ); 190 : 0 : return; 191 : : } 192 : 0 : } 193 : : #endif 194 : : 195 : : private: 196 : : #ifdef SIP_RUN 197 : : QgsPointCloudRenderContext( const QgsPointCloudRenderContext &rh ); 198 : : #endif 199 : : 200 : : QgsRenderContext &mRenderContext; 201 : : QgsVector3D mScale; 202 : : QgsVector3D mOffset; 203 : : long mPointsRendered = 0; 204 : : QgsPointCloudAttributeCollection mAttributes; 205 : : int mPointRecordSize = 0; 206 : : int mXOffset = 0; 207 : : int mYOffset = 0; 208 : : int mZOffset = 0; 209 : : double mZValueScale = 1.0; 210 : : double mZValueFixedOffset = 0; 211 : : }; 212 : : 213 : : 214 : : /** 215 : : * \ingroup core 216 : : * \class QgsPointCloudRenderer 217 : : * 218 : : * \brief Abstract base class for 2d point cloud renderers. 219 : : * 220 : : * \since QGIS 3.18 221 : : */ 222 : : class CORE_EXPORT QgsPointCloudRenderer 223 : : { 224 : : 225 : : #ifdef SIP_RUN 226 : : SIP_CONVERT_TO_SUBCLASS_CODE 227 : : 228 : : const QString type = sipCpp->type(); 229 : : 230 : : if ( type == QLatin1String( "rgb" ) ) 231 : : sipType = sipType_QgsPointCloudRgbRenderer; 232 : : else if ( type == QLatin1String( "ramp" ) ) 233 : : sipType = sipType_QgsPointCloudAttributeByRampRenderer; 234 : : else if ( type == QLatin1String( "classified" ) ) 235 : : sipType = sipType_QgsPointCloudClassifiedRenderer; 236 : : else if ( type == QLatin1String( "extent" ) ) 237 : : sipType = sipType_QgsPointCloudExtentRenderer; 238 : : else 239 : : sipType = 0; 240 : : SIP_END 241 : : #endif 242 : : 243 : : public: 244 : : 245 : : /** 246 : : * Rendering symbols for points. 247 : : */ 248 : : enum PointSymbol 249 : : { 250 : : Square, //!< Renders points as squares 251 : : Circle, //!< Renders points as circles 252 : : }; 253 : : 254 : : /** 255 : : * Constructor for QgsPointCloudRenderer. 256 : : */ 257 : 0 : QgsPointCloudRenderer() = default; 258 : : 259 : 0 : virtual ~QgsPointCloudRenderer() = default; 260 : : 261 : : /** 262 : : * Returns the identifier of the renderer type. 263 : : */ 264 : : virtual QString type() const = 0; 265 : : 266 : : /** 267 : : * Create a deep copy of this renderer. Should be implemented by all subclasses 268 : : * and generate a proper subclass. 269 : : */ 270 : : virtual QgsPointCloudRenderer *clone() const = 0 SIP_FACTORY; 271 : : 272 : : //! QgsPointCloudRenderer cannot be copied -- use clone() instead 273 : : QgsPointCloudRenderer( const QgsPointCloudRenderer &other ) = delete; 274 : : 275 : : //! QgsPointCloudRenderer cannot be copied -- use clone() instead 276 : : QgsPointCloudRenderer &operator=( const QgsPointCloudRenderer &other ) = delete; 277 : : 278 : : /** 279 : : * Renders a \a block of point cloud data using the specified render \a context. 280 : : */ 281 : : virtual void renderBlock( const QgsPointCloudBlock *block, QgsPointCloudRenderContext &context ) = 0; 282 : : 283 : : /** 284 : : * Returns the list of visible points of the point cloud layer \a layer and an extent defined by 285 : : * a geometry in the 2D plane \a geometry. 286 : : * 287 : : * The \a toleranceForPointIdentification argument can be used to specify a minimum tolerance allowable when 288 : : * identify from a point \a geometry value. This must be specified in the map units associated with the render \a context. 289 : : * 290 : : * \warning The \a geometry value must be specified in the render context's destination CRS, not the layer's native CRS! 291 : : */ 292 : : QVector<QVariantMap> identify( QgsPointCloudLayer *layer, const QgsRenderContext &context, const QgsGeometry &geometry, double toleranceForPointIdentification = 0 ) SIP_SKIP; 293 : : 294 : : /** 295 : : * Checks whether the point holding \a pointAttributes attributes will be rendered 296 : : * By default if not overridden in the subclass renderer will return true 297 : : * ( the renderer is responsible for the filtering behavior ) 298 : : */ 299 : 0 : virtual bool willRenderPoint( const QMap<QString, QVariant> &pointAttributes ) 300 : : { 301 : 0 : Q_UNUSED( pointAttributes ); 302 : 0 : return true; 303 : : } 304 : : 305 : : /** 306 : : * Creates a renderer from an XML \a element. 307 : : * 308 : : * Caller takes ownership of the returned renderer. 309 : : * 310 : : * \see save() 311 : : */ 312 : : static QgsPointCloudRenderer *load( QDomElement &element, const QgsReadWriteContext &context ) SIP_FACTORY; 313 : : 314 : : /** 315 : : * Saves the renderer configuration to an XML element. 316 : : * \see load() 317 : : */ 318 : : virtual QDomElement save( QDomDocument &doc, const QgsReadWriteContext &context ) const = 0; 319 : : 320 : : /** 321 : : * Returns a list of attributes required by this renderer. Attributes not listed in here may 322 : : * not be requested from the provider at rendering time. 323 : : * 324 : : * \note the "X" and "Y" attributes will always be fetched and do not need to be explicitly 325 : : * returned here. 326 : : */ 327 : : virtual QSet< QString > usedAttributes( const QgsPointCloudRenderContext &context ) const; 328 : : 329 : : /** 330 : : * Must be called when a new render cycle is started. A call to startRender() must always 331 : : * be followed by a corresponding call to stopRender() after all features have been rendered. 332 : : * 333 : : * \see stopRender() 334 : : * 335 : : * \warning This method is not thread safe. Before calling startRender() in a non-main thread, 336 : : * the renderer should instead be cloned and startRender()/stopRender() called on the clone. 337 : : */ 338 : : virtual void startRender( QgsPointCloudRenderContext &context ); 339 : : 340 : : /** 341 : : * Must be called when a render cycle has finished, to allow the renderer to clean up. 342 : : * 343 : : * Calls to stopRender() must always be preceded by a call to startRender(). 344 : : * 345 : : * \warning This method is not thread safe. Before calling startRender() in a non-main thread, 346 : : * the renderer should instead be cloned and startRender()/stopRender() called on the clone. 347 : : * 348 : : * \see startRender() 349 : : */ 350 : : virtual void stopRender( QgsPointCloudRenderContext &context ); 351 : : 352 : : /** 353 : : * Returns TRUE if the legend item with the specified \a key is checked. 354 : : * 355 : : * \see checkLegendItem() 356 : : */ 357 : : virtual bool legendItemChecked( const QString &key ); 358 : : 359 : : /** 360 : : * Called when the check state of the legend item with the specified \a key is changed. 361 : : * 362 : : * \see legendItemChecked() 363 : : */ 364 : : virtual void checkLegendItem( const QString &key, bool state = true ); 365 : : 366 : : /** 367 : : * Sets the point \a size. Point size units are specified via setPointSizeUnit(). 368 : : * \see pointSize() 369 : : * \see setPointSizeUnit() 370 : : * \see setPointSizeMapUnitScale() 371 : : */ 372 : 0 : void setPointSize( double size ) { mPointSize = size; } 373 : : 374 : : /** 375 : : * Returns the point size. 376 : : * 377 : : * The point size units are retrieved by calling pointSizeUnit(). 378 : : * 379 : : * \see setPointSize() 380 : : * \see pointSizeUnit() 381 : : * \see pointSizeMapUnitScale() 382 : : */ 383 : 0 : double pointSize() const { return mPointSize; } 384 : : 385 : : /** 386 : : * Sets the \a units used for the point size. 387 : : * 388 : : * \see setPointSize() 389 : : * \see pointSizeUnit() 390 : : * \see setPointSizeMapUnitScale() 391 : : */ 392 : 0 : void setPointSizeUnit( const QgsUnitTypes::RenderUnit units ) { mPointSizeUnit = units; } 393 : : 394 : : /** 395 : : * Returns the units used for the point size. 396 : : * \see setPointSizeUnit() 397 : : * \see pointSize() 398 : : * \see pointSizeMapUnitScale() 399 : : */ 400 : 0 : QgsUnitTypes::RenderUnit pointSizeUnit() const { return mPointSizeUnit; } 401 : : 402 : : /** 403 : : * Sets the map unit \a scale used for the point size. 404 : : * \see pointSizeMapUnitScale() 405 : : * \see setPointSize() 406 : : * \see setPointSizeUnit() 407 : : */ 408 : 0 : void setPointSizeMapUnitScale( const QgsMapUnitScale &scale ) { mPointSizeMapUnitScale = scale; } 409 : : 410 : : /** 411 : : * Returns the map unit scale used for the point size. 412 : : * \see setPointSizeMapUnitScale() 413 : : * \see pointSizeUnit() 414 : : * \see pointSize() 415 : : */ 416 : 0 : const QgsMapUnitScale &pointSizeMapUnitScale() const { return mPointSizeMapUnitScale; } 417 : : 418 : : /** 419 : : * Returns the symbol used by the renderer for drawing points. 420 : : * 421 : : * \see setPointSymbol() 422 : : */ 423 : : PointSymbol pointSymbol() const; 424 : : 425 : : /** 426 : : * Sets the \a symbol used by the renderer for drawing points. 427 : : * 428 : : * \see pointSymbol() 429 : : */ 430 : : void setPointSymbol( PointSymbol symbol ); 431 : : 432 : : /** 433 : : * Returns the maximum screen error allowed when rendering the point cloud. 434 : : * 435 : : * Larger values result in a faster render with less points rendered. 436 : : * 437 : : * Units are retrieved via maximumScreenErrorUnit(). 438 : : * 439 : : * \see setMaximumScreenError() 440 : : * \see maximumScreenErrorUnit() 441 : : */ 442 : : double maximumScreenError() const; 443 : : 444 : : /** 445 : : * Sets the maximum screen \a error allowed when rendering the point cloud. 446 : : * 447 : : * Larger values result in a faster render with less points rendered. 448 : : * 449 : : * Units are set via setMaximumScreenErrorUnit(). 450 : : * 451 : : * \see maximumScreenError() 452 : : * \see setMaximumScreenErrorUnit() 453 : : */ 454 : : void setMaximumScreenError( double error ); 455 : : 456 : : /** 457 : : * Returns the unit for the maximum screen error allowed when rendering the point cloud. 458 : : * 459 : : * \see maximumScreenError() 460 : : * \see setMaximumScreenErrorUnit() 461 : : */ 462 : : QgsUnitTypes::RenderUnit maximumScreenErrorUnit() const; 463 : : 464 : : /** 465 : : * Sets the \a unit for the maximum screen error allowed when rendering the point cloud. 466 : : * 467 : : * \see setMaximumScreenError() 468 : : * \see maximumScreenErrorUnit() 469 : : */ 470 : : void setMaximumScreenErrorUnit( QgsUnitTypes::RenderUnit unit ); 471 : : 472 : : /** 473 : : * Creates a set of legend nodes representing the renderer. 474 : : */ 475 : : virtual QList<QgsLayerTreeModelLegendNode *> createLegendNodes( QgsLayerTreeLayer *nodeLayer ) SIP_FACTORY; 476 : : 477 : : /** 478 : : * Returns a list of all rule keys for legend nodes created by the renderer. 479 : : */ 480 : : virtual QStringList legendRuleKeys() const; 481 : : 482 : : protected: 483 : : 484 : : /** 485 : : * Retrieves the x and y coordinate for the point at index \a i. 486 : : */ 487 : 0 : static void pointXY( QgsPointCloudRenderContext &context, const char *ptr, int i, double &x, double &y ) 488 : : { 489 : : // be wary when copying this code!! In the renderer we explicitly request x/y/z as qint32 values, but in other 490 : : // situations these may be floats or doubles! 491 : 0 : const qint32 ix = *reinterpret_cast< const qint32 * >( ptr + i * context.pointRecordSize() + context.xOffset() ); 492 : 0 : const qint32 iy = *reinterpret_cast< const qint32 * >( ptr + i * context.pointRecordSize() + context.yOffset() ); 493 : 0 : x = context.offset().x() + context.scale().x() * ix; 494 : 0 : y = context.offset().y() + context.scale().y() * iy; 495 : 0 : } 496 : : 497 : : /** 498 : : * Retrieves the z value for the point at index \a i. 499 : : */ 500 : 0 : static double pointZ( QgsPointCloudRenderContext &context, const char *ptr, int i ) 501 : : { 502 : : // be wary when copying this code!! In the renderer we explicitly request x/y/z as qint32 values, but in other 503 : : // situations these may be floats or doubles! 504 : 0 : const qint32 iz = *reinterpret_cast<const qint32 * >( ptr + i * context.pointRecordSize() + context.zOffset() ); 505 : 0 : return ( context.offset().z() + context.scale().z() * iz ) * context.zValueScale() + context.zValueFixedOffset(); 506 : : } 507 : : 508 : : /** 509 : : * Draws a point using a \a color at the specified \a x and \a y (in map coordinates). 510 : : */ 511 : 0 : void drawPoint( double x, double y, const QColor &color, QgsPointCloudRenderContext &context ) const 512 : : { 513 : 0 : QPointF originalXY( x, y ); 514 : 0 : context.renderContext().mapToPixel().transformInPlace( x, y ); 515 : 0 : QPainter *painter = context.renderContext().painter(); 516 : 0 : switch ( mPointSymbol ) 517 : : { 518 : : case Square: 519 : 0 : painter->fillRect( QRectF( x - mPainterPenWidth * 0.5, 520 : 0 : y - mPainterPenWidth * 0.5, 521 : 0 : mPainterPenWidth, mPainterPenWidth ), color ); 522 : 0 : break; 523 : : 524 : : case Circle: 525 : 0 : painter->setBrush( QBrush( color ) ); 526 : 0 : painter->setPen( Qt::NoPen ); 527 : 0 : painter->drawEllipse( QRectF( x - mPainterPenWidth * 0.5, 528 : 0 : y - mPainterPenWidth * 0.5, 529 : 0 : mPainterPenWidth, mPainterPenWidth ) ); 530 : 0 : break; 531 : : }; 532 : 0 : } 533 : : 534 : : /** 535 : : * Copies common point cloud properties (such as point size and screen error) to the \a destination renderer. 536 : : */ 537 : : void copyCommonProperties( QgsPointCloudRenderer *destination ) const; 538 : : 539 : : /** 540 : : * Restores common renderer properties (such as point size and screen error) from the 541 : : * specified DOM \a element. 542 : : * 543 : : * \see saveCommonProperties() 544 : : */ 545 : : void restoreCommonProperties( const QDomElement &element, const QgsReadWriteContext &context ); 546 : : 547 : : /** 548 : : * Saves common renderer properties (such as point size and screen error) to the 549 : : * specified DOM \a element. 550 : : * 551 : : * \see restoreCommonProperties() 552 : : */ 553 : : void saveCommonProperties( QDomElement &element, const QgsReadWriteContext &context ) const; 554 : : 555 : : private: 556 : : #ifdef SIP_RUN 557 : : QgsPointCloudRenderer( const QgsPointCloudRenderer &other ); 558 : : #endif 559 : : 560 : : #ifdef QGISDEBUG 561 : : //! Pointer to thread in which startRender was first called 562 : : QThread *mThread = nullptr; 563 : : #endif 564 : : 565 : 0 : double mMaximumScreenError = 0.3; 566 : 0 : QgsUnitTypes::RenderUnit mMaximumScreenErrorUnit = QgsUnitTypes::RenderMillimeters; 567 : : 568 : 0 : double mPointSize = 1; 569 : 0 : QgsUnitTypes::RenderUnit mPointSizeUnit = QgsUnitTypes::RenderMillimeters; 570 : : QgsMapUnitScale mPointSizeMapUnitScale; 571 : : 572 : 0 : PointSymbol mPointSymbol = Square; 573 : 0 : int mPainterPenWidth = 1; 574 : : }; 575 : : 576 : : #endif // QGSPOINTCLOUDRENDERER_H