QGIS API Documentation 3.99.0-Master (b3fe4c4eded)
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 <QtConcurrentRun>
47
48#include "moc_qgsannotationlayerchunkloader_p.cpp"
49
50using namespace Qt::StringLiterals;
51
53
54
55QgsAnnotationLayerChunkLoader::QgsAnnotationLayerChunkLoader( const QgsAnnotationLayerChunkLoaderFactory *factory, QgsChunkNode *node )
56 : QgsChunkLoader( node )
57 , mFactory( factory )
58 , mRenderContext( factory->mRenderContext )
59{
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 QgsEventTracing::ScopedEvent 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, showCallouts, rect, zOffset, altitudeClamping, this, &textBillboards, &textBillboardTexts]( const QgsPointXY &p, const QString &annotationText, const QgsTextFormat &annotationTextFormat ) {
153 QString text = annotationText;
154 if ( annotationTextFormat.allowHtmlFormatting() )
155 {
156 // strip HTML characters, we don't support those in 3D
157 const QgsTextDocument document = QgsTextDocument::fromTextAndFormat( { text }, annotationTextFormat );
158 text = document.toPlainText().join( ' ' );
159 }
160 if ( !text.isEmpty() )
161 {
162 try
163 {
164 const QgsPointXY mapPoint = layerToMapTransform.transform( p );
165 if ( !rect.contains( mapPoint ) )
166 return;
167
168 double z = 0;
169 const float terrainZ = ( altitudeClamping == Qgis::AltitudeClamping::Absolute && !showCallouts ) ? 0 : mRenderContext.terrainRenderingEnabled() && mRenderContext.terrainGenerator() ? static_cast<float>( mRenderContext.terrainGenerator()->heightAt( mapPoint.x(), mapPoint.y(), mRenderContext ) * mRenderContext.terrainSettings()->verticalScale() )
170 : 0.f;
171
172 switch ( altitudeClamping )
173 {
175 z = zOffset;
176 break;
178 z = terrainZ;
179 break;
181 z = terrainZ + zOffset;
182 break;
183 }
184
185 TextBillboard billboard;
186 billboard.position = ( QgsVector3D( mapPoint.x(), mapPoint.y(), z ) - mChunkOrigin ).toVector3D();
187 billboard.text = text;
188 textBillboards.emplace_back( std::move( billboard ) );
189 textBillboardTexts.append( text );
190
191 if ( showCallouts )
192 {
193 mCalloutLines << QgsLineString( { mapPoint.x(), mapPoint.x() }, { mapPoint.y(), mapPoint.y() }, { terrainZ, z } );
194 }
195
196 mZMax = std::max( mZMax, showCallouts ? std::max( 0.0, z ) : z );
197 mZMin = std::min( mZMin, showCallouts ? std::min( 0.0, z ) : z );
198 }
199 catch ( QgsCsException &e )
200 {
201 QgsDebugError( e.what() );
202 }
203 }
204 };
205
206 for ( const std::unique_ptr< QgsAnnotationItem > &item : std::as_const( mItemsToRender ) )
207 {
208 if ( mCanceled )
209 break;
210
211 QgsAnnotationItem *annotation = item.get();
212
213 if ( !annotation->enabled() )
214 continue;
215
216 if ( QgsAnnotationMarkerItem *marker = dynamic_cast< QgsAnnotationMarkerItem * >( annotation ) )
217 {
218 if ( marker->symbol() )
219 {
220 QgsPointXY p = marker->geometry();
221 try
222 {
223 const QgsPointXY mapPoint = layerToMapTransform.transform( p );
224 if ( !rect.contains( mapPoint ) )
225 continue;
226
227 double z = 0;
228 const float terrainZ = ( altitudeClamping == Qgis::AltitudeClamping::Absolute && !showCallouts ) ? 0 : mRenderContext.terrainRenderingEnabled() && mRenderContext.terrainGenerator() ? static_cast<float>( mRenderContext.terrainGenerator()->heightAt( mapPoint.x(), mapPoint.y(), mRenderContext ) * mRenderContext.terrainSettings()->verticalScale() )
229 : 0.f;
230
231 switch ( altitudeClamping )
232 {
234 z = zOffset;
235 break;
237 z = terrainZ;
238 break;
240 z = terrainZ + zOffset;
241 break;
242 }
243
244 Billboard billboard;
245 billboard.position = ( QgsVector3D( mapPoint.x(), mapPoint.y(), z ) - mChunkOrigin ).toVector3D();
246 billboard.textureId = -1;
247
248 for ( const Billboard &existingBillboard : billboards )
249 {
250 if ( existingBillboard.markerSymbol && marker->symbol()->rendersIdenticallyTo( existingBillboard.markerSymbol ) )
251 {
252 // marker symbol has been reused => reuse existing texture to minimize size of texture atlas
253 billboard.textureId = existingBillboard.textureId;
254 break;
255 }
256 }
257
258 if ( billboard.textureId < 0 )
259 {
260 // could not match to previously considered marker, have to render and add to texture atlas
261 billboard.markerSymbol = marker->symbol();
262 billboard.textureId = textures.size();
263 textures.append( QgsPoint3DBillboardMaterial::renderSymbolToImage( marker->symbol(), mRenderContext ) );
264 }
265 billboards.emplace_back( std::move( billboard ) );
266
267 if ( showCallouts )
268 {
269 mCalloutLines << QgsLineString( { mapPoint.x(), mapPoint.x() }, { mapPoint.y(), mapPoint.y() }, { terrainZ, z } );
270 }
271
272 mZMax = std::max( mZMax, showCallouts ? std::max( 0.0, z ) : z );
273 mZMin = std::min( mZMin, showCallouts ? std::min( 0.0, z ) : z );
274 }
275 catch ( QgsCsException &e )
276 {
277 QgsDebugError( e.what() );
278 }
279 }
280 }
281 else if ( QgsAnnotationPointTextItem *pointText = dynamic_cast< QgsAnnotationPointTextItem * >( annotation ) )
282 {
283 addTextBillboard( pointText->point(), pointText->text(), pointText->format() );
284 }
285 else if ( QgsAnnotationLineTextItem *lineText = dynamic_cast< QgsAnnotationLineTextItem * >( annotation ) )
286 {
287 QgsGeos geos( lineText->geometry() );
288 std::unique_ptr< QgsPoint > point( geos.pointOnSurface() );
289 if ( point )
290 {
291 addTextBillboard( *point, lineText->text(), lineText->format() );
292 }
293 }
294 else if ( QgsAnnotationRectangleTextItem *rectText = dynamic_cast< QgsAnnotationRectangleTextItem * >( annotation ) )
295 {
296 switch ( rectText->placementMode() )
297 {
300 {
301 addTextBillboard( rectText->bounds().center(), rectText->text(), rectText->format() );
302 break;
303 }
305 // ignore these annotations, they don't have a fix map position
306 break;
307 }
308 }
309 }
310 // free memory
311 mItemsToRender.clear();
312
313 if ( !textures.isEmpty() )
314 {
315 const QgsTextureAtlas atlas = QgsTextureAtlasGenerator::createFromImages( textures, 2048 );
316 if ( atlas.isValid() )
317 {
318 mBillboardAtlas = atlas.renderAtlasTexture();
319 mBillboardPositions.reserve( static_cast< int >( billboards.size() ) );
320 for ( Billboard &billboard : billboards )
321 {
322 const QRect textureRect = atlas.rect( billboard.textureId );
324 geometry.position = billboard.position;
325 geometry.textureAtlasOffset = QVector2D( static_cast< float >( textureRect.left() ) / static_cast< float>( mBillboardAtlas.width() ), 1 - ( static_cast< float >( textureRect.bottom() ) / static_cast< float>( mBillboardAtlas.height() ) ) );
326 geometry.textureAtlasSize = QVector2D( static_cast< float >( textureRect.width() ) / static_cast< float>( mBillboardAtlas.width() ), static_cast< float>( textureRect.height() ) / static_cast< float>( mBillboardAtlas.height() ) );
327 geometry.pixelOffset = QPoint( 0, textureRect.height() / 2 );
328 mBillboardPositions.append( geometry );
329 }
330 }
331 else
332 {
333 QgsDebugError( u"Error encountered building texture atlas"_s );
334 mBillboardAtlas = QImage();
335 }
336 }
337 else
338 {
339 mBillboardAtlas = QImage();
340 mBillboardPositions.clear();
341 }
342
343
344 if ( !textBillboardTexts.isEmpty() )
345 {
346 const QgsFontTextureAtlas atlas = QgsFontTextureAtlasGenerator::create( textFormat, textBillboardTexts );
347 if ( atlas.isValid() )
348 {
349 mTextBillboardAtlas = atlas.renderAtlasTexture();
350 mTextBillboardPositions.reserve( static_cast< int >( textBillboards.size() ) );
351 for ( TextBillboard &billboard : textBillboards )
352 {
353 int graphemeIndex = 0;
354 const int graphemeCount = atlas.graphemeCount( billboard.text );
355 // horizontally center text over point
356 const double xOffset = atlas.totalWidth( billboard.text ) / 2.0;
357 for ( ; graphemeIndex < graphemeCount; ++graphemeIndex )
358 {
359 const QRect textureRect = atlas.textureRectForGrapheme( billboard.text, graphemeIndex );
361 geometry.position = billboard.position;
362 geometry.textureAtlasOffset = QVector2D( static_cast< float >( textureRect.left() ) / static_cast< float>( mTextBillboardAtlas.width() ), 1 - ( static_cast< float >( textureRect.bottom() ) / static_cast< float>( mTextBillboardAtlas.height() ) ) );
363 geometry.textureAtlasSize = QVector2D( static_cast< float >( textureRect.width() ) / static_cast< float>( mTextBillboardAtlas.width() ), static_cast< float>( textureRect.height() ) / static_cast< float>( mTextBillboardAtlas.height() ) );
364 const QPointF pixelOffset = atlas.pixelOffsetForGrapheme( billboard.text, graphemeIndex );
365 geometry.pixelOffset = QPoint( static_cast< int >( std::round( -xOffset + pixelOffset.x() + 0.5 * textureRect.width() ) ), static_cast< int >( std::round( pixelOffset.y() + 0.5 * textureRect.height() ) ) );
366 mTextBillboardPositions.append( geometry );
367 }
368 }
369 }
370 else
371 {
372 QgsDebugError( u"Error encountered building font texture atlas"_s );
373 mTextBillboardAtlas = QImage();
374 }
375 }
376 else
377 {
378 mTextBillboardAtlas = QImage();
379 mTextBillboardPositions.clear();
380 }
381 } );
382
383 // emit finished() as soon as the handler is populated with features
384 mFutureWatcher->setFuture( future );
385}
386
387QgsAnnotationLayerChunkLoader::~QgsAnnotationLayerChunkLoader()
388{
389 if ( mFutureWatcher && !mFutureWatcher->isFinished() )
390 {
391 disconnect( mFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsChunkQueueJob::finished );
392 mFutureWatcher->waitForFinished();
393 }
394}
395
396void QgsAnnotationLayerChunkLoader::cancel()
397{
398 mCanceled = true;
399}
400
401Qt3DCore::QEntity *QgsAnnotationLayerChunkLoader::createEntity( Qt3DCore::QEntity *parent )
402{
403 if ( mNode->level() < mFactory->mLeafLevel )
404 {
405 Qt3DCore::QEntity *entity = new Qt3DCore::QEntity( parent ); // dummy entity
406 entity->setObjectName( mLayerName + "_CONTAINER_" + mNode->tileId().text() );
407 return entity;
408 }
409
410 if ( mBillboardPositions.empty() && mTextBillboardPositions.empty() )
411 {
412 // an empty node, so we return no entity. This tags the node as having no data and effectively removes it.
413 // we just make sure first that its initial estimated vertical range does not affect its parents' bboxes calculation
414 mNode->setExactBox3D( QgsBox3D() );
415 mNode->updateParentBoundingBoxesRecursively();
416 return nullptr;
417 }
418
419 Qt3DCore::QEntity *entity = new Qt3DCore::QEntity( parent );
420 entity->setObjectName( mLayerName + "_" + mNode->tileId().text() );
421
422 QgsGeoTransform *billboardTransform = new QgsGeoTransform;
423 billboardTransform->setGeoTranslation( mChunkOrigin );
424 entity->addComponent( billboardTransform );
425
426 if ( !mBillboardPositions.empty() )
427 {
428 QgsBillboardGeometry *billboardGeometry = new QgsBillboardGeometry();
429 billboardGeometry->setBillboardData( mBillboardPositions, true );
430
431 Qt3DRender::QGeometryRenderer *billboardGeometryRenderer = new Qt3DRender::QGeometryRenderer;
432 billboardGeometryRenderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::Points );
433 billboardGeometryRenderer->setGeometry( billboardGeometry );
434 billboardGeometryRenderer->setVertexCount( billboardGeometry->count() );
435
437 billboardMaterial->setTexture2DFromImage( mBillboardAtlas );
438
439
440 Qt3DCore::QEntity *billboardEntity = new Qt3DCore::QEntity;
441 billboardEntity->addComponent( billboardMaterial );
442 billboardEntity->addComponent( billboardGeometryRenderer );
443 billboardEntity->setParent( entity );
444 }
445
446 if ( !mTextBillboardPositions.empty() )
447 {
448 QgsBillboardGeometry *textBillboardGeometry = new QgsBillboardGeometry();
449 textBillboardGeometry->setBillboardData( mTextBillboardPositions, true );
450
451 Qt3DRender::QGeometryRenderer *billboardGeometryRenderer = new Qt3DRender::QGeometryRenderer;
452 billboardGeometryRenderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::Points );
453 billboardGeometryRenderer->setGeometry( textBillboardGeometry );
454 billboardGeometryRenderer->setVertexCount( textBillboardGeometry->count() );
455
457 billboardMaterial->setTexture2DFromImage( mTextBillboardAtlas );
458
459 Qt3DCore::QEntity *billboardEntity = new Qt3DCore::QEntity;
460 billboardEntity->addComponent( billboardMaterial );
461 billboardEntity->addComponent( billboardGeometryRenderer );
462 billboardEntity->setParent( entity );
463 }
464
465
466 if ( mFactory->mShowCallouts )
467 {
468 QgsLineVertexData lineData;
469 lineData.withAdjacency = true;
470 lineData.geocentricCoordinates = false; // mMapSettings->sceneMode() == Qgis::SceneMode::Globe;
471 lineData.init( Qgis::AltitudeClamping::Absolute, Qgis::AltitudeBinding::Vertex, 0, mRenderContext, mChunkOrigin );
472
473 for ( const QgsLineString &line : mCalloutLines )
474 {
475 lineData.addLineString( line, 0, false );
476 }
477
478 QgsLineMaterial *mat = new QgsLineMaterial;
479 mat->setLineColor( mFactory->mCalloutLineColor );
480 mat->setLineWidth( mFactory->mCalloutLineWidth );
481
482 Qt3DCore::QEntity *calloutEntity = new Qt3DCore::QEntity;
483 calloutEntity->setObjectName( parent->objectName() + "_CALLOUTS" );
484
485 // geometry renderer
486 Qt3DRender::QGeometryRenderer *calloutRenderer = new Qt3DRender::QGeometryRenderer;
487 calloutRenderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::LineStripAdjacency );
488 calloutRenderer->setGeometry( lineData.createGeometry( calloutEntity ) );
489 calloutRenderer->setVertexCount( lineData.indexes.count() );
490 calloutRenderer->setPrimitiveRestartEnabled( true );
491 calloutRenderer->setRestartIndexValue( 0 );
492
493 // make entity
494 calloutEntity->addComponent( calloutRenderer );
495 calloutEntity->addComponent( mat );
496
497 calloutEntity->setParent( entity );
498 }
499
500 // fix the vertical range of the node from the estimated vertical range to the true range
501 if ( mZMin != std::numeric_limits<float>::max() && mZMax != std::numeric_limits<float>::lowest() )
502 {
503 QgsBox3D box = mNode->box3D();
504 box.setZMinimum( mZMin );
505 box.setZMaximum( mZMax );
506 mNode->setExactBox3D( box );
507 mNode->updateParentBoundingBoxesRecursively();
508 }
509 return entity;
510}
511
512
514
515
516QgsAnnotationLayerChunkLoaderFactory::QgsAnnotationLayerChunkLoaderFactory( const Qgs3DRenderContext &context, QgsAnnotationLayer *layer, int leafLevel, Qgis::AltitudeClamping clamping, double zOffset, bool showCallouts, const QColor &calloutLineColor, double calloutLineWidth, const QgsTextFormat &textFormat, double zMin, double zMax )
517 : mRenderContext( context )
518 , mLayer( layer )
519 , mLeafLevel( leafLevel )
520 , mClamping( clamping )
521 , mZOffset( zOffset )
522 , mShowCallouts( showCallouts )
523 , mCalloutLineColor( calloutLineColor )
524 , mCalloutLineWidth( calloutLineWidth )
525 , mTextFormat( textFormat )
526{
527 if ( context.crs().type() == Qgis::CrsType::Geocentric )
528 {
529 // TODO: add support for handling of annotation layers
530 // (we're using dummy quadtree here to make sure the empty extent does not break the scene completely)
531 QgsDebugError( u"Annotation layers in globe scenes are not supported yet!"_s );
532 setupQuadtree( QgsBox3D( -1e7, -1e7, -1e7, 1e7, 1e7, 1e7 ), -1, leafLevel );
533 return;
534 }
535
536 QgsBox3D rootBox3D( context.extent(), zMin, zMax );
537 // add small padding to avoid clipping of point features located at the edge of the bounding box
538 rootBox3D.grow( 1.0 );
539 setupQuadtree( rootBox3D, -1, leafLevel ); // negative root error means that the node does not contain anything
540}
541
542QgsChunkLoader *QgsAnnotationLayerChunkLoaderFactory::createChunkLoader( QgsChunkNode *node ) const
543{
544 return new QgsAnnotationLayerChunkLoader( this, node );
545}
546
547
549
550
551QgsAnnotationLayerChunkedEntity::QgsAnnotationLayerChunkedEntity( Qgs3DMapSettings *map, QgsAnnotationLayer *layer, Qgis::AltitudeClamping clamping, double zOffset, bool showCallouts, const QColor &calloutLineColor, double calloutLineWidth, const QgsTextFormat &textFormat, double zMin, double zMax )
552 : QgsChunkedEntity( map,
553 -1, // max. allowed screen error (negative tau means that we need to go until leaves are reached)
554 new QgsAnnotationLayerChunkLoaderFactory( Qgs3DRenderContext::fromMapSettings( map ), layer, 3, clamping, zOffset, showCallouts, calloutLineColor, calloutLineWidth, textFormat, zMin, zMax ), true )
555{
556 mTransform = new Qt3DCore::QTransform;
557 if ( applyTerrainOffset() )
558 {
559 mTransform->setTranslation( QVector3D( 0.0f, 0.0f, static_cast<float>( map->terrainSettings()->elevationOffset() ) ) );
560 }
561 this->addComponent( mTransform );
562
563 connect( map, &Qgs3DMapSettings::terrainSettingsChanged, this, &QgsAnnotationLayerChunkedEntity::onTerrainElevationOffsetChanged );
564}
565
566QgsAnnotationLayerChunkedEntity::~QgsAnnotationLayerChunkedEntity()
567{
568 // cancel / wait for jobs
569 cancelActiveJobs();
570}
571
572// if the AltitudeClamping is `Absolute`, do not apply the offset
573bool QgsAnnotationLayerChunkedEntity::applyTerrainOffset() const
574{
575 if ( auto loaderFactory = static_cast<QgsAnnotationLayerChunkLoaderFactory *>( mChunkLoaderFactory ) )
576 {
577 return loaderFactory->mClamping != Qgis::AltitudeClamping::Absolute;
578 }
579 return true;
580}
581
582void QgsAnnotationLayerChunkedEntity::onTerrainElevationOffsetChanged()
583{
584 QgsDebugMsgLevel( u"QgsAnnotationLayerChunkedEntity::onTerrainElevationOffsetChanged"_s, 2 );
585 float newOffset = static_cast<float>( qobject_cast<Qgs3DMapSettings *>( sender() )->terrainSettings()->elevationOffset() );
586 if ( !applyTerrainOffset() )
587 {
588 newOffset = 0.0;
589 }
590 mTransform->setTranslation( QVector3D( 0.0f, 0.0f, newOffset ) );
591}
592
593
AltitudeClamping
Altitude clamping.
Definition qgis.h:4053
@ Relative
Elevation is relative to terrain height (final elevation = terrain elevation + feature elevation).
Definition qgis.h:4055
@ Terrain
Elevation is clamped to terrain (final elevation = terrain elevation).
Definition qgis.h:4056
@ Absolute
Elevation is taken directly from feature and is independent of terrain height (final elevation = feat...
Definition qgis.h:4054
@ Geocentric
Geocentric CRS.
Definition qgis.h:2388
@ Vertex
Clamp every vertex of feature.
Definition qgis.h:4067
@ SpatialBounds
Item is rendered inside fixed spatial bounds, and size will depend on map scale.
Definition qgis.h:2539
@ FixedSize
Item is rendered at a fixed size, regardless of map scale. Item's location is georeferenced to a spat...
Definition qgis.h:2540
@ RelativeToMapFrame
Items size and placement is relative to the map's frame, and the item will always be rendered in the ...
Definition qgis.h:2541
@ Reverse
Reverse/inverse transform (from destination to source).
Definition qgis.h:2731
Definition of the world.
const QgsAbstractTerrainSettings * terrainSettings() const
Returns the terrain settings.
void terrainSettingsChanged()
Emitted when the terrain settings are changed.
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.
double elevationOffset() const
Returns the elevation offset of the terrain (used to move the terrain up or down).
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
void grow(double delta)
Grows the box in place by the specified amount in all dimensions.
Definition qgsbox3d.cpp:307
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:141
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 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:77
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:63
#define QgsDebugError(str)
Definition qgslogger.h:59
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.