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