QGIS API Documentation 3.99.0-Master (7d2ca374f2d)
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
63
64struct Billboard
65{
66 QVector3D position;
67 int textureId = -1;
68 const QgsMarkerSymbol *markerSymbol = nullptr;
69};
70
71struct TextBillboard
72{
73 QVector3D position;
74 QString text;
75};
76
77
78void QgsAnnotationLayerChunkLoader::start()
79{
80 QgsChunkNode *node = chunk();
81 if ( node->level() < mFactory->mLeafLevel )
82 {
83 QTimer::singleShot( 0, this, &QgsAnnotationLayerChunkLoader::finished );
84 return;
85 }
86
87 QgsAnnotationLayer *layer = mFactory->mLayer;
88 mLayerName = mFactory->mLayer->name();
89
90 // only a subset of data to be queried
91 const QgsRectangle rect = node->box3D().toRectangle();
92 // origin for coordinates of the chunk - it is kind of arbitrary, but it should be
93 // picked so that the coordinates are relatively small to avoid numerical precision issues
94 mChunkOrigin = QgsVector3D( rect.center().x(), rect.center().y(), 0 );
95
96 QgsExpressionContext exprContext;
98 mRenderContext.setExpressionContext( exprContext );
99
100 QgsCoordinateTransform layerToMapTransform( layer->crs(), mRenderContext.crs(), mRenderContext.transformContext() );
101
102 QgsRectangle layerExtent;
103 try
104 {
105 layerExtent = layerToMapTransform.transformBoundingBox( rect, Qgis::TransformDirection::Reverse );
106 }
107 catch ( QgsCsException &e )
108 {
109 QgsDebugError( u"Error transforming annotation layer extent to 3d map extent: %1"_s.arg( e.what() ) );
110 return;
111 }
112
113 const double zOffset = mFactory->mZOffset;
114 const Qgis::AltitudeClamping altitudeClamping = mFactory->mClamping;
115 bool showCallouts = mFactory->mShowCallouts;
116 const QgsTextFormat textFormat = mFactory->mTextFormat;
117
118 // see logic from QgsAnnotationLayerRenderer
119 const QStringList itemsList = layer->queryIndex( layerExtent );
120 QSet< QString > itemIds( itemsList.begin(), itemsList.end() );
121
122 // we also have NO choice but to clone ALL non-indexed items (i.e. those with a scale-dependent bounding box)
123 // since these won't be in the layer's spatial index, and it's too expensive to determine their actual bounding box
124 // upfront (we are blocking the main thread right now!)
125
126 // TODO -- come up with some brilliant way to avoid this and also index scale-dependent items ;)
127 itemIds.unite( layer->mNonIndexedItems );
128
129 mItemsToRender.reserve( itemIds.size() );
130 std::transform( itemIds.begin(), itemIds.end(), std::back_inserter( mItemsToRender ), [layer]( const QString &id ) -> std::unique_ptr< QgsAnnotationItem > {
131 return std::unique_ptr< QgsAnnotationItem >( layer->item( id )->clone() );
132 } );
133
134 //
135 // this will be run in a background thread
136 //
137 mFutureWatcher = new QFutureWatcher<void>( this );
138 connect( mFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsChunkQueueJob::finished );
139
140 const QFuture<void> future = QtConcurrent::run( [this, rect, layerToMapTransform, zOffset, altitudeClamping, showCallouts, textFormat] {
141 const QgsEventTracing::ScopedEvent e( u"3D"_s, u"Annotation layer chunk load"_s );
142
143 std::vector< Billboard > billboards;
144 billboards.reserve( mItemsToRender.size() );
145 QVector< QImage > textures;
146 textures.reserve( mItemsToRender.size() );
147
148 std::vector< TextBillboard > textBillboards;
149 textBillboards.reserve( mItemsToRender.size() );
150 QStringList textBillboardTexts;
151 textBillboardTexts.reserve( mItemsToRender.size() );
152
153 auto addTextBillboard = [layerToMapTransform, showCallouts, rect, zOffset, altitudeClamping, this, &textBillboards, &textBillboardTexts]( const QgsPointXY &p, const QString &annotationText, const QgsTextFormat &annotationTextFormat ) {
154 QString text = annotationText;
155 if ( annotationTextFormat.allowHtmlFormatting() )
156 {
157 // strip HTML characters, we don't support those in 3D
158 const QgsTextDocument document = QgsTextDocument::fromTextAndFormat( { text }, annotationTextFormat );
159 text = document.toPlainText().join( ' ' );
160 }
161 if ( !text.isEmpty() )
162 {
163 try
164 {
165 const QgsPointXY mapPoint = layerToMapTransform.transform( p );
166 if ( !rect.contains( mapPoint ) )
167 return;
168
169 double z = 0;
170 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() )
171 : 0.f;
172
173 switch ( altitudeClamping )
174 {
176 z = zOffset;
177 break;
179 z = terrainZ;
180 break;
182 z = terrainZ + zOffset;
183 break;
184 }
185
186 TextBillboard billboard;
187 billboard.position = ( QgsVector3D( mapPoint.x(), mapPoint.y(), z ) - mChunkOrigin ).toVector3D();
188 billboard.text = text;
189 textBillboards.emplace_back( std::move( billboard ) );
190 textBillboardTexts.append( text );
191
192 if ( showCallouts )
193 {
194 mCalloutLines << QgsLineString( { mapPoint.x(), mapPoint.x() }, { mapPoint.y(), mapPoint.y() }, { terrainZ, z } );
195 }
196
197 mZMax = std::max( mZMax, showCallouts ? std::max( 0.0, z ) : z );
198 mZMin = std::min( mZMin, showCallouts ? std::min( 0.0, z ) : z );
199 }
200 catch ( QgsCsException &e )
201 {
202 QgsDebugError( e.what() );
203 }
204 }
205 };
206
207 for ( const std::unique_ptr< QgsAnnotationItem > &item : std::as_const( mItemsToRender ) )
208 {
209 if ( mCanceled )
210 break;
211
212 QgsAnnotationItem *annotation = item.get();
213
214 if ( !annotation->enabled() )
215 continue;
216
217 if ( QgsAnnotationMarkerItem *marker = dynamic_cast< QgsAnnotationMarkerItem * >( annotation ) )
218 {
219 if ( marker->symbol() )
220 {
221 QgsPointXY p = marker->geometry();
222 try
223 {
224 const QgsPointXY mapPoint = layerToMapTransform.transform( p );
225 if ( !rect.contains( mapPoint ) )
226 continue;
227
228 double z = 0;
229 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() )
230 : 0.f;
231
232 switch ( altitudeClamping )
233 {
235 z = zOffset;
236 break;
238 z = terrainZ;
239 break;
241 z = terrainZ + zOffset;
242 break;
243 }
244
245 Billboard billboard;
246 billboard.position = ( QgsVector3D( mapPoint.x(), mapPoint.y(), z ) - mChunkOrigin ).toVector3D();
247 billboard.textureId = -1;
248
249 for ( const Billboard &existingBillboard : billboards )
250 {
251 if ( existingBillboard.markerSymbol && marker->symbol()->rendersIdenticallyTo( existingBillboard.markerSymbol ) )
252 {
253 // marker symbol has been reused => reuse existing texture to minimize size of texture atlas
254 billboard.textureId = existingBillboard.textureId;
255 break;
256 }
257 }
258
259 if ( billboard.textureId < 0 )
260 {
261 // could not match to previously considered marker, have to render and add to texture atlas
262 billboard.markerSymbol = marker->symbol();
263 billboard.textureId = textures.size();
264 textures.append( QgsPoint3DBillboardMaterial::renderSymbolToImage( marker->symbol(), mRenderContext ) );
265 }
266 billboards.emplace_back( std::move( billboard ) );
267
268 if ( showCallouts )
269 {
270 mCalloutLines << QgsLineString( { mapPoint.x(), mapPoint.x() }, { mapPoint.y(), mapPoint.y() }, { terrainZ, z } );
271 }
272
273 mZMax = std::max( mZMax, showCallouts ? std::max( 0.0, z ) : z );
274 mZMin = std::min( mZMin, showCallouts ? std::min( 0.0, z ) : z );
275 }
276 catch ( QgsCsException &e )
277 {
278 QgsDebugError( e.what() );
279 }
280 }
281 }
282 else if ( QgsAnnotationPointTextItem *pointText = dynamic_cast< QgsAnnotationPointTextItem * >( annotation ) )
283 {
284 addTextBillboard( pointText->point(), pointText->text(), pointText->format() );
285 }
286 else if ( QgsAnnotationLineTextItem *lineText = dynamic_cast< QgsAnnotationLineTextItem * >( annotation ) )
287 {
288 QgsGeos geos( lineText->geometry() );
289 std::unique_ptr< QgsPoint > point( geos.pointOnSurface() );
290 if ( point )
291 {
292 addTextBillboard( *point, lineText->text(), lineText->format() );
293 }
294 }
295 else if ( QgsAnnotationRectangleTextItem *rectText = dynamic_cast< QgsAnnotationRectangleTextItem * >( annotation ) )
296 {
297 switch ( rectText->placementMode() )
298 {
301 {
302 addTextBillboard( rectText->bounds().center(), rectText->text(), rectText->format() );
303 break;
304 }
306 // ignore these annotations, they don't have a fix map position
307 break;
308 }
309 }
310 }
311 // free memory
312 mItemsToRender.clear();
313
314 if ( !textures.isEmpty() )
315 {
316 const QgsTextureAtlas atlas = QgsTextureAtlasGenerator::createFromImages( textures, 2048 );
317 if ( atlas.isValid() )
318 {
319 mBillboardAtlas = atlas.renderAtlasTexture();
320 mBillboardPositions.reserve( static_cast< int >( billboards.size() ) );
321 for ( Billboard &billboard : billboards )
322 {
323 const QRect textureRect = atlas.rect( billboard.textureId );
325 geometry.position = billboard.position;
326 geometry.textureAtlasOffset = QVector2D( static_cast< float >( textureRect.left() ) / static_cast< float>( mBillboardAtlas.width() ), 1 - ( static_cast< float >( textureRect.bottom() ) / static_cast< float>( mBillboardAtlas.height() ) ) );
327 geometry.textureAtlasSize = QVector2D( static_cast< float >( textureRect.width() ) / static_cast< float>( mBillboardAtlas.width() ), static_cast< float>( textureRect.height() ) / static_cast< float>( mBillboardAtlas.height() ) );
328 geometry.pixelOffset = QPoint( 0, textureRect.height() / 2 );
329 mBillboardPositions.append( geometry );
330 }
331 }
332 else
333 {
334 QgsDebugError( u"Error encountered building texture atlas"_s );
335 mBillboardAtlas = QImage();
336 }
337 }
338 else
339 {
340 mBillboardAtlas = QImage();
341 mBillboardPositions.clear();
342 }
343
344
345 if ( !textBillboardTexts.isEmpty() )
346 {
347 const QgsFontTextureAtlas atlas = QgsFontTextureAtlasGenerator::create( textFormat, textBillboardTexts );
348 if ( atlas.isValid() )
349 {
350 mTextBillboardAtlas = atlas.renderAtlasTexture();
351 mTextBillboardPositions.reserve( static_cast< int >( textBillboards.size() ) );
352 for ( TextBillboard &billboard : textBillboards )
353 {
354 int graphemeIndex = 0;
355 const int graphemeCount = atlas.graphemeCount( billboard.text );
356 // horizontally center text over point
357 const double xOffset = atlas.totalWidth( billboard.text ) / 2.0;
358 for ( ; graphemeIndex < graphemeCount; ++graphemeIndex )
359 {
360 const QRect textureRect = atlas.textureRectForGrapheme( billboard.text, graphemeIndex );
362 geometry.position = billboard.position;
363 geometry.textureAtlasOffset = QVector2D( static_cast< float >( textureRect.left() ) / static_cast< float>( mTextBillboardAtlas.width() ), 1 - ( static_cast< float >( textureRect.bottom() ) / static_cast< float>( mTextBillboardAtlas.height() ) ) );
364 geometry.textureAtlasSize = QVector2D( static_cast< float >( textureRect.width() ) / static_cast< float>( mTextBillboardAtlas.width() ), static_cast< float>( textureRect.height() ) / static_cast< float>( mTextBillboardAtlas.height() ) );
365 const QPointF pixelOffset = atlas.pixelOffsetForGrapheme( billboard.text, graphemeIndex );
366 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() ) ) );
367 mTextBillboardPositions.append( geometry );
368 }
369 }
370 }
371 else
372 {
373 QgsDebugError( u"Error encountered building font texture atlas"_s );
374 mTextBillboardAtlas = QImage();
375 }
376 }
377 else
378 {
379 mTextBillboardAtlas = QImage();
380 mTextBillboardPositions.clear();
381 }
382 } );
383
384 // emit finished() as soon as the handler is populated with features
385 mFutureWatcher->setFuture( future );
386}
387
388QgsAnnotationLayerChunkLoader::~QgsAnnotationLayerChunkLoader()
389{
390 if ( mFutureWatcher && !mFutureWatcher->isFinished() )
391 {
392 disconnect( mFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsChunkQueueJob::finished );
393 mFutureWatcher->waitForFinished();
394 }
395}
396
397void QgsAnnotationLayerChunkLoader::cancel()
398{
399 mCanceled = true;
400}
401
402Qt3DCore::QEntity *QgsAnnotationLayerChunkLoader::createEntity( Qt3DCore::QEntity *parent )
403{
404 if ( mNode->level() < mFactory->mLeafLevel )
405 {
406 Qt3DCore::QEntity *entity = new Qt3DCore::QEntity( parent ); // dummy entity
407 entity->setObjectName( mLayerName + "_CONTAINER_" + mNode->tileId().text() );
408 return entity;
409 }
410
411 if ( mBillboardPositions.empty() && mTextBillboardPositions.empty() )
412 {
413 // an empty node, so we return no entity. This tags the node as having no data and effectively removes it.
414 // we just make sure first that its initial estimated vertical range does not affect its parents' bboxes calculation
415 mNode->setExactBox3D( QgsBox3D() );
416 mNode->updateParentBoundingBoxesRecursively();
417 return nullptr;
418 }
419
420 Qt3DCore::QEntity *entity = new Qt3DCore::QEntity( parent );
421 entity->setObjectName( mLayerName + "_" + mNode->tileId().text() );
422
423 QgsGeoTransform *billboardTransform = new QgsGeoTransform;
424 billboardTransform->setGeoTranslation( mChunkOrigin );
425 entity->addComponent( billboardTransform );
426
427 if ( !mBillboardPositions.empty() )
428 {
429 QgsBillboardGeometry *billboardGeometry = new QgsBillboardGeometry();
430 billboardGeometry->setBillboardData( mBillboardPositions, true );
431
432 Qt3DRender::QGeometryRenderer *billboardGeometryRenderer = new Qt3DRender::QGeometryRenderer;
433 billboardGeometryRenderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::Points );
434 billboardGeometryRenderer->setGeometry( billboardGeometry );
435 billboardGeometryRenderer->setVertexCount( billboardGeometry->count() );
436
438 billboardMaterial->setTexture2DFromImage( mBillboardAtlas );
439
440
441 Qt3DCore::QEntity *billboardEntity = new Qt3DCore::QEntity;
442 billboardEntity->addComponent( billboardMaterial );
443 billboardEntity->addComponent( billboardGeometryRenderer );
444 billboardEntity->setParent( entity );
445 }
446
447 if ( !mTextBillboardPositions.empty() )
448 {
449 QgsBillboardGeometry *textBillboardGeometry = new QgsBillboardGeometry();
450 textBillboardGeometry->setBillboardData( mTextBillboardPositions, true );
451
452 Qt3DRender::QGeometryRenderer *billboardGeometryRenderer = new Qt3DRender::QGeometryRenderer;
453 billboardGeometryRenderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::Points );
454 billboardGeometryRenderer->setGeometry( textBillboardGeometry );
455 billboardGeometryRenderer->setVertexCount( textBillboardGeometry->count() );
456
458 billboardMaterial->setTexture2DFromImage( mTextBillboardAtlas );
459
460 Qt3DCore::QEntity *billboardEntity = new Qt3DCore::QEntity;
461 billboardEntity->addComponent( billboardMaterial );
462 billboardEntity->addComponent( billboardGeometryRenderer );
463 billboardEntity->setParent( entity );
464 }
465
466
467 if ( mFactory->mShowCallouts )
468 {
469 QgsLineVertexData lineData;
470 lineData.withAdjacency = true;
471 lineData.geocentricCoordinates = false; // mMapSettings->sceneMode() == Qgis::SceneMode::Globe;
472 lineData.init( Qgis::AltitudeClamping::Absolute, Qgis::AltitudeBinding::Vertex, 0, mRenderContext, mChunkOrigin );
473
474 for ( const QgsLineString &line : mCalloutLines )
475 {
476 lineData.addLineString( line, 0, false );
477 }
478
479 QgsLineMaterial *mat = new QgsLineMaterial;
480 mat->setLineColor( mFactory->mCalloutLineColor );
481 mat->setLineWidth( mFactory->mCalloutLineWidth );
482
483 Qt3DCore::QEntity *calloutEntity = new Qt3DCore::QEntity;
484 calloutEntity->setObjectName( parent->objectName() + "_CALLOUTS" );
485
486 // geometry renderer
487 Qt3DRender::QGeometryRenderer *calloutRenderer = new Qt3DRender::QGeometryRenderer;
488 calloutRenderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::LineStripAdjacency );
489 calloutRenderer->setGeometry( lineData.createGeometry( calloutEntity ) );
490 calloutRenderer->setVertexCount( lineData.indexes.count() );
491 calloutRenderer->setPrimitiveRestartEnabled( true );
492 calloutRenderer->setRestartIndexValue( 0 );
493
494 // make entity
495 calloutEntity->addComponent( calloutRenderer );
496 calloutEntity->addComponent( mat );
497
498 calloutEntity->setParent( entity );
499 }
500
501 // fix the vertical range of the node from the estimated vertical range to the true range
502 if ( mZMin != std::numeric_limits<float>::max() && mZMax != std::numeric_limits<float>::lowest() )
503 {
504 QgsBox3D box = mNode->box3D();
505 box.setZMinimum( mZMin );
506 box.setZMaximum( mZMax );
507 mNode->setExactBox3D( box );
508 mNode->updateParentBoundingBoxesRecursively();
509 }
510 return entity;
511}
512
513
515
516
517QgsAnnotationLayerChunkLoaderFactory::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 )
518 : mRenderContext( context )
519 , mLayer( layer )
520 , mLeafLevel( leafLevel )
521 , mClamping( clamping )
522 , mZOffset( zOffset )
523 , mShowCallouts( showCallouts )
524 , mCalloutLineColor( calloutLineColor )
525 , mCalloutLineWidth( calloutLineWidth )
526 , mTextFormat( textFormat )
527{
528 if ( context.crs().type() == Qgis::CrsType::Geocentric )
529 {
530 // TODO: add support for handling of annotation layers
531 // (we're using dummy quadtree here to make sure the empty extent does not break the scene completely)
532 QgsDebugError( u"Annotation layers in globe scenes are not supported yet!"_s );
533 setupQuadtree( QgsBox3D( -1e7, -1e7, -1e7, 1e7, 1e7, 1e7 ), -1, leafLevel );
534 return;
535 }
536
537 QgsBox3D rootBox3D( context.extent(), zMin, zMax );
538 // add small padding to avoid clipping of point features located at the edge of the bounding box
539 rootBox3D.grow( 1.0 );
540 setupQuadtree( rootBox3D, -1, leafLevel ); // negative root error means that the node does not contain anything
541}
542
543QgsChunkLoader *QgsAnnotationLayerChunkLoaderFactory::createChunkLoader( QgsChunkNode *node ) const
544{
545 return new QgsAnnotationLayerChunkLoader( this, node );
546}
547
548
550
551
552QgsAnnotationLayerChunkedEntity::QgsAnnotationLayerChunkedEntity( Qgs3DMapSettings *map, QgsAnnotationLayer *layer, Qgis::AltitudeClamping clamping, double zOffset, bool showCallouts, const QColor &calloutLineColor, double calloutLineWidth, const QgsTextFormat &textFormat, double zMin, double zMax )
553 : QgsChunkedEntity( map,
554 -1, // max. allowed screen error (negative tau means that we need to go until leaves are reached)
555 new QgsAnnotationLayerChunkLoaderFactory( Qgs3DRenderContext::fromMapSettings( map ), layer, 3, clamping, zOffset, showCallouts, calloutLineColor, calloutLineWidth, textFormat, zMin, zMax ), true )
556{
557 mTransform = new Qt3DCore::QTransform;
558 if ( applyTerrainOffset() )
559 {
560 mTransform->setTranslation( QVector3D( 0.0f, 0.0f, static_cast<float>( map->terrainSettings()->elevationOffset() ) ) );
561 }
562 this->addComponent( mTransform );
563
564 connect( map, &Qgs3DMapSettings::terrainSettingsChanged, this, &QgsAnnotationLayerChunkedEntity::onTerrainElevationOffsetChanged );
565}
566
567QgsAnnotationLayerChunkedEntity::~QgsAnnotationLayerChunkedEntity()
568{
569 // cancel / wait for jobs
570 cancelActiveJobs();
571}
572
573// if the AltitudeClamping is `Absolute`, do not apply the offset
574bool QgsAnnotationLayerChunkedEntity::applyTerrainOffset() const
575{
576 if ( auto loaderFactory = static_cast<QgsAnnotationLayerChunkLoaderFactory *>( mChunkLoaderFactory ) )
577 {
578 return loaderFactory->mClamping != Qgis::AltitudeClamping::Absolute;
579 }
580 return true;
581}
582
583void QgsAnnotationLayerChunkedEntity::onTerrainElevationOffsetChanged()
584{
585 QgsDebugMsgLevel( u"QgsAnnotationLayerChunkedEntity::onTerrainElevationOffsetChanged"_s, 2 );
586 float newOffset = static_cast<float>( qobject_cast<Qgs3DMapSettings *>( sender() )->terrainSettings()->elevationOffset() );
587 if ( !applyTerrainOffset() )
588 {
589 newOffset = 0.0;
590 }
591 mTransform->setTranslation( QVector3D( 0.0f, 0.0f, newOffset ) );
592}
593
594
AltitudeClamping
Altitude clamping.
Definition qgis.h:4068
@ Relative
Elevation is relative to terrain height (final elevation = terrain elevation + feature elevation).
Definition qgis.h:4070
@ Terrain
Elevation is clamped to terrain (final elevation = terrain elevation).
Definition qgis.h:4071
@ Absolute
Elevation is taken directly from feature and is independent of terrain height (final elevation = feat...
Definition qgis.h:4069
@ Geocentric
Geocentric CRS.
Definition qgis.h:2403
@ Vertex
Clamp every vertex of feature.
Definition qgis.h:4082
@ SpatialBounds
Item is rendered inside fixed spatial bounds, and size will depend on map scale.
Definition qgis.h:2554
@ FixedSize
Item is rendered at a fixed size, regardless of map scale. Item's location is georeferenced to a spat...
Definition qgis.h:2555
@ RelativeToMapFrame
Items size and placement is relative to the map's frame, and the item will always be rendered in the ...
Definition qgis.h:2556
@ Reverse
Reverse/inverse transform (from destination to source).
Definition qgis.h:2746
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.