QGIS API Documentation 4.3.0-Master (03a7e469fa9)
Loading...
Searching...
No Matches
qgsannotationlayerchunkloader_p.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsannotationlayerchunkloader_p.cpp
3 --------------------------------------
4 Date : September 2025
5 Copyright : (C) 2025 by Nyall Dawson
6 Email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
17
18#include "qgs3dutils.h"
19#include "qgsabstract3dsymbol.h"
21#include "qgsannotationitem.h"
22#include "qgsannotationlayer.h"
28#include "qgschunknode.h"
29#include "qgseventtracing.h"
32#include "qgsgeos.h"
33#include "qgsgeotransform.h"
34#include "qgslinematerial_p.h"
35#include "qgslinevertexdata_p.h"
36#include "qgslogger.h"
37#include "qgsmarkersymbol.h"
40#include "qgstextdocument.h"
42
43#include <QString>
44#include <QTimer>
45#include <Qt3DCore/QTransform>
46#include <Qt3DRender/QGeometryRenderer>
47#include <QtConcurrentRun>
48
49#include "moc_qgsannotationlayerchunkloader_p.cpp"
50
51using namespace Qt::StringLiterals;
52
54
55
56QgsAnnotationLayerChunkLoader::QgsAnnotationLayerChunkLoader( const QgsAnnotationLayerChunkLoaderFactory *factory, QgsChunkNode *node )
57 : QgsChunkLoader( node )
58 , mFactory( factory )
59 , mRenderContext( factory->mRenderContext )
60{}
61
62
63struct Billboard
64{
65 QVector3D position;
66 int textureId = -1;
67 const QgsMarkerSymbol *markerSymbol = nullptr;
68};
69
70struct TextBillboard
71{
72 QVector3D position;
73 QString text;
74};
75
76
77void QgsAnnotationLayerChunkLoader::start()
78{
79 QgsChunkNode *node = chunk();
80 if ( node->level() < mFactory->mLeafLevel )
81 {
82 QTimer::singleShot( 0, this, &QgsAnnotationLayerChunkLoader::finished );
83 return;
84 }
85
86 QgsAnnotationLayer *layer = mFactory->mLayer;
87 mLayerName = mFactory->mLayer->name();
88
89 // only a subset of data to be queried
90 const QgsRectangle rect = node->box3D().toRectangle();
91 // origin for coordinates of the chunk - it is kind of arbitrary, but it should be
92 // picked so that the coordinates are relatively small to avoid numerical precision issues
93 mChunkOrigin = QgsVector3D( rect.center().x(), rect.center().y(), 0 );
94
95 QgsExpressionContext exprContext;
97 mRenderContext.setExpressionContext( exprContext );
98
99 QgsCoordinateTransform layerToMapTransform( layer->crs(), mRenderContext.crs(), mRenderContext.transformContext() );
100
101 QgsRectangle layerExtent;
102 try
103 {
104 layerExtent = layerToMapTransform.transformBoundingBox( rect, Qgis::TransformDirection::Reverse );
105 }
106 catch ( QgsCsException &e )
107 {
108 QgsDebugError( u"Error transforming annotation layer extent to 3d map extent: %1"_s.arg( e.what() ) );
109 return;
110 }
111
112 const double zOffset = mFactory->mZOffset;
113 const Qgis::AltitudeClamping altitudeClamping = mFactory->mClamping;
114 bool showCallouts = mFactory->mShowCallouts;
115 const QgsTextFormat textFormat = mFactory->mTextFormat;
116
117 // see logic from QgsAnnotationLayerRenderer
118 const QStringList itemsList = layer->queryIndex( layerExtent );
119 QSet< QString > itemIds( itemsList.begin(), itemsList.end() );
120
121 // we also have NO choice but to clone ALL non-indexed items (i.e. those with a scale-dependent bounding box)
122 // since these won't be in the layer's spatial index, and it's too expensive to determine their actual bounding box
123 // upfront (we are blocking the main thread right now!)
124
125 // TODO -- come up with some brilliant way to avoid this and also index scale-dependent items ;)
126 itemIds.unite( layer->mNonIndexedItems );
127
128 mItemsToRender.reserve( itemIds.size() );
129 std::transform( itemIds.begin(), itemIds.end(), std::back_inserter( mItemsToRender ), [layer]( const QString &id ) -> std::unique_ptr< QgsAnnotationItem > {
130 return std::unique_ptr< QgsAnnotationItem >( layer->item( id )->clone() );
131 } );
132
133 //
134 // this will be run in a background thread
135 //
136 mFutureWatcher = new QFutureWatcher<void>( this );
137 connect( mFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsChunkQueueJob::finished );
138
139 const QFuture<void> future = QtConcurrent::run( [this, rect, layerToMapTransform, zOffset, altitudeClamping, showCallouts, textFormat] {
140 const QgsScopedEvent e( u"3D"_s, u"Annotation layer chunk load"_s );
141
142 std::vector< Billboard > billboards;
143 billboards.reserve( mItemsToRender.size() );
144 QVector< QImage > textures;
145 textures.reserve( mItemsToRender.size() );
146
147 std::vector< TextBillboard > textBillboards;
148 textBillboards.reserve( mItemsToRender.size() );
149 QStringList textBillboardTexts;
150 textBillboardTexts.reserve( mItemsToRender.size() );
151
152 auto addTextBillboard = [layerToMapTransform,
153 showCallouts,
154 rect,
155 zOffset,
156 altitudeClamping,
157 this,
158 &textBillboards,
159 &textBillboardTexts]( const QgsPointXY &p, const QString &annotationText, const QgsTextFormat &annotationTextFormat ) {
160 QString text = annotationText;
161 if ( annotationTextFormat.allowHtmlFormatting() )
162 {
163 // strip HTML characters, we don't support those in 3D
164 const QgsTextDocument document = QgsTextDocument::fromTextAndFormat( { text }, annotationTextFormat );
165 text = document.toPlainText().join( ' ' );
166 }
167 if ( !text.isEmpty() )
168 {
169 try
170 {
171 const QgsPointXY mapPoint = layerToMapTransform.transform( p );
172 if ( !rect.contains( mapPoint ) )
173 return;
174
175 double z = 0;
176 const float terrainZ = ( altitudeClamping == Qgis::AltitudeClamping::Absolute && !showCallouts ) ? 0
177 : mRenderContext.terrainRenderingEnabled() && mRenderContext.terrainGenerator()
178 ? static_cast<float>( mRenderContext.terrainGenerator()->heightAt( mapPoint.x(), mapPoint.y(), mRenderContext ) * mRenderContext.terrainSettings()->verticalScale() )
179 : 0.f;
180
181 switch ( altitudeClamping )
182 {
184 z = zOffset;
185 break;
187 z = terrainZ;
188 break;
190 z = terrainZ + zOffset;
191 break;
192 }
193
194 TextBillboard billboard;
195 billboard.position = ( QgsVector3D( mapPoint.x(), mapPoint.y(), z ) - mChunkOrigin ).toVector3D();
196 billboard.text = text;
197 textBillboards.emplace_back( std::move( billboard ) );
198 textBillboardTexts.append( text );
199
200 if ( showCallouts )
201 {
202 mCalloutLines << QgsLineString( { mapPoint.x(), mapPoint.x() }, { mapPoint.y(), mapPoint.y() }, { terrainZ, z } );
203 }
204
205 mZMax = std::max( mZMax, showCallouts ? std::max( 0.0, z ) : z );
206 mZMin = std::min( mZMin, showCallouts ? std::min( 0.0, z ) : z );
207 }
208 catch ( QgsCsException &e )
209 {
210 QgsDebugError( e.what() );
211 }
212 }
213 };
214
215 for ( const std::unique_ptr< QgsAnnotationItem > &item : std::as_const( mItemsToRender ) )
216 {
217 if ( mCanceled )
218 break;
219
220 QgsAnnotationItem *annotation = item.get();
221
222 if ( !annotation->enabled() )
223 continue;
224
225 if ( QgsAnnotationMarkerItem *marker = dynamic_cast< QgsAnnotationMarkerItem * >( annotation ) )
226 {
227 if ( marker->symbol() )
228 {
229 QgsPointXY p = marker->geometry();
230 try
231 {
232 const QgsPointXY mapPoint = layerToMapTransform.transform( p );
233 if ( !rect.contains( mapPoint ) )
234 continue;
235
236 double z = 0;
237 const float terrainZ = ( altitudeClamping == Qgis::AltitudeClamping::Absolute && !showCallouts ) ? 0
238 : mRenderContext.terrainRenderingEnabled() && mRenderContext.terrainGenerator()
239 ? static_cast<float>( mRenderContext.terrainGenerator()->heightAt( mapPoint.x(), mapPoint.y(), mRenderContext ) * mRenderContext.terrainSettings()->verticalScale() )
240 : 0.f;
241
242 switch ( altitudeClamping )
243 {
245 z = zOffset;
246 break;
248 z = terrainZ;
249 break;
251 z = terrainZ + zOffset;
252 break;
253 }
254
255 Billboard billboard;
256 billboard.position = ( QgsVector3D( mapPoint.x(), mapPoint.y(), z ) - mChunkOrigin ).toVector3D();
257 billboard.textureId = -1;
258
259 for ( const Billboard &existingBillboard : billboards )
260 {
261 if ( existingBillboard.markerSymbol && marker->symbol()->rendersIdenticallyTo( existingBillboard.markerSymbol ) )
262 {
263 // marker symbol has been reused => reuse existing texture to minimize size of texture atlas
264 billboard.textureId = existingBillboard.textureId;
265 break;
266 }
267 }
268
269 if ( billboard.textureId < 0 )
270 {
271 // could not match to previously considered marker, have to render and add to texture atlas
272 billboard.markerSymbol = marker->symbol();
273 billboard.textureId = textures.size();
274 textures.append( QgsPoint3DBillboardMaterial::renderSymbolToImage( marker->symbol(), mRenderContext ) );
275 }
276 billboards.emplace_back( std::move( billboard ) );
277
278 if ( showCallouts )
279 {
280 mCalloutLines << QgsLineString( { mapPoint.x(), mapPoint.x() }, { mapPoint.y(), mapPoint.y() }, { terrainZ, z } );
281 }
282
283 mZMax = std::max( mZMax, showCallouts ? std::max( 0.0, z ) : z );
284 mZMin = std::min( mZMin, showCallouts ? std::min( 0.0, z ) : z );
285 }
286 catch ( QgsCsException &e )
287 {
288 QgsDebugError( e.what() );
289 }
290 }
291 }
292 else if ( QgsAnnotationPointTextItem *pointText = dynamic_cast< QgsAnnotationPointTextItem * >( annotation ) )
293 {
294 addTextBillboard( pointText->point(), pointText->text(), pointText->format() );
295 }
296 else if ( QgsAnnotationLineTextItem *lineText = dynamic_cast< QgsAnnotationLineTextItem * >( annotation ) )
297 {
298 QgsGeos geos( lineText->geometry() );
299 std::unique_ptr< QgsPoint > point( geos.pointOnSurface() );
300 if ( point )
301 {
302 addTextBillboard( *point, lineText->text(), lineText->format() );
303 }
304 }
305 else if ( QgsAnnotationRectangleTextItem *rectText = dynamic_cast< QgsAnnotationRectangleTextItem * >( annotation ) )
306 {
307 switch ( rectText->placementMode() )
308 {
311 {
312 addTextBillboard( rectText->bounds().center(), rectText->text(), rectText->format() );
313 break;
314 }
316 // ignore these annotations, they don't have a fix map position
317 break;
318 }
319 }
320 }
321 // free memory
322 mItemsToRender.clear();
323
324 if ( !textures.isEmpty() )
325 {
326 const QgsTextureAtlas atlas = QgsTextureAtlasGenerator::createFromImages( textures, 2048 );
327 if ( atlas.isValid() )
328 {
329 mBillboardAtlas = atlas.renderAtlasTexture();
330 mBillboardPositions.reserve( static_cast< int >( billboards.size() ) );
331 for ( Billboard &billboard : billboards )
332 {
333 const QRect textureRect = atlas.rect( billboard.textureId );
335 geometry.position = billboard.position;
336 geometry.textureAtlasOffset = QVector2D(
337 static_cast< float >( textureRect.left() ) / static_cast< float>( mBillboardAtlas.width() ),
338 1 - ( static_cast< float >( textureRect.bottom() ) / static_cast< float>( mBillboardAtlas.height() ) )
339 );
340 geometry.textureAtlasSize = QVector2D(
341 static_cast< float >( textureRect.width() ) / static_cast< float>( mBillboardAtlas.width() ), static_cast< float>( textureRect.height() ) / static_cast< float>( mBillboardAtlas.height() )
342 );
343 geometry.pixelOffset = QPoint( 0, textureRect.height() / 2 );
344 mBillboardPositions.append( geometry );
345 }
346 }
347 else
348 {
349 QgsDebugError( u"Error encountered building texture atlas"_s );
350 mBillboardAtlas = QImage();
351 }
352 }
353 else
354 {
355 mBillboardAtlas = QImage();
356 mBillboardPositions.clear();
357 }
358
359
360 if ( !textBillboardTexts.isEmpty() )
361 {
362 const QgsFontTextureAtlas atlas = QgsFontTextureAtlasGenerator::create( textFormat, textBillboardTexts );
363 if ( atlas.isValid() )
364 {
365 mTextBillboardAtlas = atlas.renderAtlasTexture();
366 mTextBillboardPositions.reserve( static_cast< int >( textBillboards.size() ) );
367 for ( TextBillboard &billboard : textBillboards )
368 {
369 int graphemeIndex = 0;
370 const int graphemeCount = atlas.graphemeCount( billboard.text );
371 // horizontally center text over point
372 const double xOffset = atlas.totalWidth( billboard.text ) / 2.0;
373 for ( ; graphemeIndex < graphemeCount; ++graphemeIndex )
374 {
375 const QRect textureRect = atlas.textureRectForGrapheme( billboard.text, graphemeIndex );
377 geometry.position = billboard.position;
378 geometry.textureAtlasOffset = QVector2D(
379 static_cast< float >( textureRect.left() ) / static_cast< float>( mTextBillboardAtlas.width() ),
380 1 - ( static_cast< float >( textureRect.bottom() ) / static_cast< float>( mTextBillboardAtlas.height() ) )
381 );
382 geometry.textureAtlasSize = QVector2D(
383 static_cast< float >( textureRect.width() ) / static_cast< float>( mTextBillboardAtlas.width() ),
384 static_cast< float>( textureRect.height() ) / static_cast< float>( mTextBillboardAtlas.height() )
385 );
386 const QPointF pixelOffset = atlas.pixelOffsetForGrapheme( billboard.text, graphemeIndex );
387 geometry.pixelOffset
388 = QPoint( static_cast< int >( std::round( -xOffset + pixelOffset.x() + 0.5 * textureRect.width() ) ), static_cast< int >( std::round( pixelOffset.y() + 0.5 * textureRect.height() ) ) );
389 mTextBillboardPositions.append( geometry );
390 }
391 }
392 }
393 else
394 {
395 QgsDebugError( u"Error encountered building font texture atlas"_s );
396 mTextBillboardAtlas = QImage();
397 }
398 }
399 else
400 {
401 mTextBillboardAtlas = QImage();
402 mTextBillboardPositions.clear();
403 }
404 } );
405
406 // emit finished() as soon as the handler is populated with features
407 mFutureWatcher->setFuture( future );
408}
409
410QgsAnnotationLayerChunkLoader::~QgsAnnotationLayerChunkLoader()
411{
412 if ( mFutureWatcher && !mFutureWatcher->isFinished() )
413 {
414 disconnect( mFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsChunkQueueJob::finished );
415 mFutureWatcher->waitForFinished();
416 }
417}
418
419void QgsAnnotationLayerChunkLoader::cancel()
420{
421 mCanceled = true;
422}
423
424Qt3DCore::QEntity *QgsAnnotationLayerChunkLoader::createEntity( Qt3DCore::QEntity *parent )
425{
426 if ( mNode->level() < mFactory->mLeafLevel )
427 {
428 Qt3DCore::QEntity *entity = new Qt3DCore::QEntity( parent ); // dummy entity
429 entity->setObjectName( mLayerName + "_CONTAINER_" + mNode->tileId().text() );
430 return entity;
431 }
432
433 if ( mBillboardPositions.empty() && mTextBillboardPositions.empty() )
434 {
435 // an empty node, so we return no entity. This tags the node as having no data and effectively removes it.
436 // we just make sure first that its initial estimated vertical range does not affect its parents' bboxes calculation
437 mNode->setExactBox3D( QgsBox3D() );
438 mNode->updateParentBoundingBoxesRecursively();
439 return nullptr;
440 }
441
442 Qt3DCore::QEntity *entity = new Qt3DCore::QEntity( parent );
443 entity->setObjectName( mLayerName + "_" + mNode->tileId().text() );
444
445 QgsGeoTransform *billboardTransform = new QgsGeoTransform;
446 billboardTransform->setGeoTranslation( mChunkOrigin );
447 entity->addComponent( billboardTransform );
448
449 if ( !mBillboardPositions.empty() )
450 {
451 QgsBillboardGeometry *billboardGeometry = new QgsBillboardGeometry();
452 billboardGeometry->setBillboardData( mBillboardPositions, true );
453
454 Qt3DRender::QGeometryRenderer *billboardGeometryRenderer = new Qt3DRender::QGeometryRenderer;
455 billboardGeometryRenderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::TriangleStrip );
456 billboardGeometryRenderer->setGeometry( billboardGeometry );
457 billboardGeometryRenderer->setVertexCount( 4 );
458 billboardGeometryRenderer->setInstanceCount( mBillboardPositions.count() );
459
461 billboardMaterial->setTexture2DFromImage( mBillboardAtlas );
462
463
464 Qt3DCore::QEntity *billboardEntity = new Qt3DCore::QEntity;
465 billboardEntity->addComponent( billboardMaterial );
466 billboardEntity->addComponent( billboardGeometryRenderer );
467 billboardEntity->setParent( entity );
468 }
469
470 if ( !mTextBillboardPositions.empty() )
471 {
472 QgsBillboardGeometry *textBillboardGeometry = new QgsBillboardGeometry();
473 textBillboardGeometry->setBillboardData( mTextBillboardPositions, true );
474
475 Qt3DRender::QGeometryRenderer *billboardGeometryRenderer = new Qt3DRender::QGeometryRenderer;
476 billboardGeometryRenderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::TriangleStrip );
477 billboardGeometryRenderer->setGeometry( textBillboardGeometry );
478 billboardGeometryRenderer->setVertexCount( 4 );
479 billboardGeometryRenderer->setInstanceCount( mTextBillboardPositions.count() );
480
482 billboardMaterial->setTexture2DFromImage( mTextBillboardAtlas );
483
484 Qt3DCore::QEntity *billboardEntity = new Qt3DCore::QEntity;
485 billboardEntity->addComponent( billboardMaterial );
486 billboardEntity->addComponent( billboardGeometryRenderer );
487 billboardEntity->setParent( entity );
488 }
489
490
491 if ( mFactory->mShowCallouts )
492 {
493 QgsLineVertexData lineData;
494 lineData.withAdjacency = true;
495 lineData.geocentricCoordinates = false; // mMapSettings->sceneMode() == Qgis::SceneMode::Globe;
496 lineData.init( Qgis::AltitudeClamping::Absolute, Qgis::AltitudeBinding::Vertex, 0, mRenderContext, mChunkOrigin );
497
498 for ( const QgsLineString &line : mCalloutLines )
499 {
500 lineData.addLineString( line, 0, false );
501 }
502
503 QgsLineMaterial *mat = new QgsLineMaterial;
504 mat->setLineColor( mFactory->mCalloutLineColor );
505 mat->setLineWidth( mFactory->mCalloutLineWidth );
506
507 Qt3DCore::QEntity *calloutEntity = new Qt3DCore::QEntity;
508 calloutEntity->setObjectName( parent->objectName() + "_CALLOUTS" );
509
510 // geometry renderer
511 Qt3DRender::QGeometryRenderer *calloutRenderer = new Qt3DRender::QGeometryRenderer;
512 calloutRenderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::LineStripAdjacency );
513 calloutRenderer->setGeometry( lineData.createGeometry( calloutEntity ) );
514 calloutRenderer->setVertexCount( lineData.indexes.count() );
515 calloutRenderer->setPrimitiveRestartEnabled( true );
516 calloutRenderer->setRestartIndexValue( 0 );
517
518 // make entity
519 calloutEntity->addComponent( calloutRenderer );
520 calloutEntity->addComponent( mat );
521
522 calloutEntity->setParent( entity );
523 }
524
525 // fix the vertical range of the node from the estimated vertical range to the true range
526 if ( mZMin != std::numeric_limits<float>::max() && mZMax != std::numeric_limits<float>::lowest() )
527 {
528 QgsBox3D box = mNode->box3D();
529 box.setZMinimum( mZMin );
530 box.setZMaximum( mZMax );
531 mNode->setExactBox3D( box );
532 mNode->updateParentBoundingBoxesRecursively();
533 }
534 return entity;
535}
536
537
539
540
541QgsAnnotationLayerChunkLoaderFactory::QgsAnnotationLayerChunkLoaderFactory(
542 const Qgs3DRenderContext &context,
543 QgsAnnotationLayer *layer,
544 int leafLevel,
545 Qgis::AltitudeClamping clamping,
546 double zOffset,
547 bool showCallouts,
548 const QColor &calloutLineColor,
549 double calloutLineWidth,
550 const QgsTextFormat &textFormat,
551 double zMin,
552 double zMax
553)
554 : mRenderContext( context )
555 , mLayer( layer )
556 , mLeafLevel( leafLevel )
557 , mClamping( clamping )
558 , mZOffset( zOffset )
559 , mShowCallouts( showCallouts )
560 , mCalloutLineColor( calloutLineColor )
561 , mCalloutLineWidth( calloutLineWidth )
562 , mTextFormat( textFormat )
563{
564 if ( context.crs().type() == Qgis::CrsType::Geocentric )
565 {
566 // TODO: add support for handling of annotation layers
567 // (we're using dummy quadtree here to make sure the empty extent does not break the scene completely)
568 QgsDebugError( u"Annotation layers in globe scenes are not supported yet!"_s );
569 setupQuadtree( QgsBox3D( -1e7, -1e7, -1e7, 1e7, 1e7, 1e7 ), -1, leafLevel );
570 return;
571 }
572
573 // choose the smaller root extent between context and mLayer ones:
574 QgsRectangle extent = context.extent();
575 const QgsRectangle layerExtentInMapCrs = Qgs3DUtils::tryReprojectExtent2D( mLayer->extent(), mLayer->crs(), context.crs(), context.transformContext() );
576 if ( layerExtentInMapCrs.isValid() )
577 {
578 extent = context.extent().intersect( layerExtentInMapCrs );
579 }
580 if ( extent.isValid() )
581 {
582 QgsBox3D rootBox3D( extent, zMin, zMax );
583
584 // add small padding to avoid clipping of point features located at the edge of the bounding box
585 rootBox3D.grow( 1.0 );
586 setupQuadtree( rootBox3D, -1, leafLevel ); // negative root error means that the node does not contain anything
587 }
588}
589
590QgsChunkLoader *QgsAnnotationLayerChunkLoaderFactory::createChunkLoader( QgsChunkNode *node ) const
591{
592 return new QgsAnnotationLayerChunkLoader( this, node );
593}
594
595
597
598
599QgsAnnotationLayerChunkedEntity::QgsAnnotationLayerChunkedEntity(
600 Qgs3DMapSettings *map,
601 QgsAnnotationLayer *layer,
602 Qgis::AltitudeClamping clamping,
603 double zOffset,
604 bool showCallouts,
605 const QColor &calloutLineColor,
606 double calloutLineWidth,
607 const QgsTextFormat &textFormat,
608 double zMin,
609 double zMax
610)
611 : QgsAbstractFeatureBasedChunkedEntity(
612 map,
613 -1, // max. allowed screen error (negative tau means that we need to go until leaves are reached)
614 new QgsAnnotationLayerChunkLoaderFactory( Qgs3DRenderContext::fromMapSettings( map ), layer, 3, clamping, zOffset, showCallouts, calloutLineColor, calloutLineWidth, textFormat, zMin, zMax ),
615 true
616 )
617{
618 onTerrainElevationOffsetChanged();
619}
620
621QgsAnnotationLayerChunkedEntity::~QgsAnnotationLayerChunkedEntity()
622{
623 // cancel / wait for jobs
624 cancelActiveJobs();
625}
626
627// if the AltitudeClamping is `Absolute`, do not apply the offset
628bool QgsAnnotationLayerChunkedEntity::applyTerrainOffset() const
629{
630 if ( auto loaderFactory = static_cast<QgsAnnotationLayerChunkLoaderFactory *>( mChunkLoaderFactory ) )
631 {
632 return loaderFactory->mClamping != Qgis::AltitudeClamping::Absolute;
633 }
634 return true;
635}
636
637QList<QgsRayCastHit> QgsAnnotationLayerChunkedEntity::rayIntersection( const QgsRay3D &ray, const QgsRayCastContext &context ) const
638{
639 Q_UNUSED( ray )
640 Q_UNUSED( context )
641 return {};
642}
643
644
AltitudeClamping
Altitude clamping.
Definition qgis.h:4196
@ Relative
Elevation is relative to terrain height (final elevation = terrain elevation + feature elevation).
Definition qgis.h:4198
@ Terrain
Elevation is clamped to terrain (final elevation = terrain elevation).
Definition qgis.h:4199
@ Absolute
Elevation is taken directly from feature and is independent of terrain height (final elevation = feat...
Definition qgis.h:4197
@ Geocentric
Geocentric CRS.
Definition qgis.h:2495
@ Vertex
Clamp every vertex of feature.
Definition qgis.h:4210
@ SpatialBounds
Item is rendered inside fixed spatial bounds, and size will depend on map scale.
Definition qgis.h:2667
@ FixedSize
Item is rendered at a fixed size, regardless of map scale. Item's location is georeferenced to a spat...
Definition qgis.h:2668
@ RelativeToMapFrame
Items size and placement is relative to the map's frame, and the item will always be rendered in the ...
Definition qgis.h:2669
@ Reverse
Reverse/inverse transform (from destination to source).
Definition qgis.h:2863
Definition of the world.
Rendering context for preparation of 3D entities.
QgsCoordinateReferenceSystem crs() const
Returns the coordinate reference system used in the 3D scene.
QgsRectangle extent() const
Returns the 3D scene's 2D extent in the 3D scene's CRS.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context, which stores various information regarding which datum tran...
static QgsRectangle tryReprojectExtent2D(const QgsRectangle &extent, const QgsCoordinateReferenceSystem &crs1, const QgsCoordinateReferenceSystem &crs2, const QgsCoordinateTransformContext &context)
Reprojects extent from crs1 to crs2 coordinate reference system with context context.
Abstract base class for annotation items which are drawn with QgsAnnotationLayers.
bool enabled() const
Returns true if the item is enabled and will be rendered in the layer.
Represents a map layer containing a set of georeferenced annotations, e.g.
An annotation item which renders text along a line geometry.
An annotation item which renders a marker symbol at a point location.
An annotation item which renders a text string at a point location.
An annotation item which renders paragraphs of text within a rectangle.
Geometry of the billboard rendering for points in 3D map view.
void setBillboardData(const QVector< QgsBillboardGeometry::BillboardAtlasData > &billboards, bool includePixelOffsets=false)
Set the position and texture data for the billboard.
A 3-dimensional box composed of x, y, z coordinates.
Definition qgsbox3d.h:45
void setZMinimum(double z)
Sets the minimum z value.
Definition qgsbox3d.cpp:94
void setZMaximum(double z)
Sets the maximum z value.
Definition qgsbox3d.cpp:99
Qgis::CrsType type() const
Returns the type of the CRS.
Handles coordinate transforms between two coordinate systems.
Custom exception class for Coordinate Reference System related exceptions.
QString what() const
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void appendScopes(const QList< QgsExpressionContextScope * > &scopes)
Appends a list of scopes to the end of the context.
static QgsFontTextureAtlas create(const QgsTextFormat &format, const QStringList &strings)
Creates the texture atlas for a set of strings, using the specified text format.
Encapsulates a font texture atlas.
int graphemeCount(const QString &string) const
Returns the number of graphemes to render for a given string.
bool isValid() const
Returns true if the atlas is valid.
QImage renderAtlasTexture() const
Renders the combined texture atlas, containing all required characters.
int totalWidth(const QString &string) const
Returns the total width (in pixels) required for a given string.
QRect textureRectForGrapheme(const QString &string, int graphemeIndex) const
Returns the packed rectangle for the texture for the matching grapheme.
QPoint pixelOffsetForGrapheme(const QString &string, int graphemeIndex) const
Returns the pixel offset at which the texture for the matching grapheme should be placed.
Does vector analysis using the GEOS library and handles import, export, and exception handling.
Definition qgsgeos.h:175
Line string geometry type, with support for z-dimension and m-values.
QString name
Definition qgsmaplayer.h:87
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:90
Material of the billboard rendering for points in 3D map view.
@ AtlasTextureWithPixelOffsets
Use a texture atlas, so each billboard has a different texture. Billboards have pixel-sized offsets f...
void setTexture2DFromImage(const QImage &image)
Set the texture2D of the billboard from an image.
Represents a 2D point.
Definition qgspointxy.h:62
double y
Definition qgspointxy.h:66
double x
Definition qgspointxy.h:65
A representation of a ray in 3D.
Definition qgsray3d.h:31
Responsible for defining parameters of the ray casting operations in 3D map canvases.
A rectangle specified with double values.
QgsPointXY center
Represents a document consisting of one or more QgsTextBlock objects.
QStringList toPlainText() const
Returns a list of plain text lines of text representing the document.
static QgsTextDocument fromTextAndFormat(const QStringList &lines, const QgsTextFormat &format)
Constructor for QgsTextDocument consisting of a set of lines, respecting settings from a text format.
Container for all settings relating to text rendering.
static QgsTextureAtlas createFromImages(const QVector< QImage > &images, int maxSide=1000)
Creates a texture atlas for a set of images.
Encapsulates a texture atlas.
bool isValid() const
Returns true if the atlas is valid.
QRect rect(int index) const
Returns the packed rectangle for the texture with the specified index.
QImage renderAtlasTexture() const
Renders the combined texture atlas, containing all source images.
A 3D vector (similar to QVector3D) with the difference that it uses double precision instead of singl...
Definition qgsvector3d.h:33
Contains geos related utilities and functions.
Definition qgsgeos.h:112
#define QgsDebugError(str)
Definition qgslogger.h:71
Contains the billboard positions and texture information.
QPoint pixelOffset
Optional pixel offset for billboard.
QVector3D position
Vertex position for billboard placement.
QVector2D textureAtlasOffset
Texture atlas offset for associated billboard texture.
QVector2D textureAtlasSize
Texture atlas size for associated billboard texture.