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