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