45#include <Qt3DCore/QTransform>
46#include <Qt3DRender/QGeometryRenderer>
47#include <QtConcurrentRun>
49#include "moc_qgsannotationlayerchunkloader_p.cpp"
51using namespace Qt::StringLiterals;
56QgsAnnotationLayerChunkLoader::QgsAnnotationLayerChunkLoader(
const QgsAnnotationLayerChunkLoaderFactory *factory, QgsChunkNode *node )
57 : QgsChunkLoader( node )
59 , mRenderContext( factory->mRenderContext )
67 const QgsMarkerSymbol *markerSymbol =
nullptr;
77void QgsAnnotationLayerChunkLoader::start()
79 QgsChunkNode *node = chunk();
80 if ( node->level() < mFactory->mLeafLevel )
82 QTimer::singleShot( 0,
this, &QgsAnnotationLayerChunkLoader::finished );
87 mLayerName = mFactory->mLayer->
name();
97 mRenderContext.setExpressionContext( exprContext );
108 QgsDebugError( u
"Error transforming annotation layer extent to 3d map extent: %1"_s.arg( e.
what() ) );
112 const double zOffset = mFactory->mZOffset;
114 bool showCallouts = mFactory->mShowCallouts;
118 const QStringList itemsList = layer->queryIndex( layerExtent );
119 QSet< QString > itemIds( itemsList.begin(), itemsList.end() );
126 itemIds.unite( layer->mNonIndexedItems );
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() );
136 mFutureWatcher =
new QFutureWatcher<void>(
this );
137 connect( mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsChunkQueueJob::finished );
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 );
142 std::vector< Billboard > billboards;
143 billboards.reserve( mItemsToRender.size() );
144 QVector< QImage > textures;
145 textures.reserve( mItemsToRender.size() );
147 std::vector< TextBillboard > textBillboards;
148 textBillboards.reserve( mItemsToRender.size() );
149 QStringList textBillboardTexts;
150 textBillboardTexts.reserve( mItemsToRender.size() );
152 auto addTextBillboard = [layerToMapTransform,
159 &textBillboardTexts](
const QgsPointXY &p,
const QString &annotationText,
const QgsTextFormat &annotationTextFormat ) {
160 QString text = annotationText;
161 if ( annotationTextFormat.allowHtmlFormatting() )
167 if ( !text.isEmpty() )
171 const QgsPointXY mapPoint = layerToMapTransform.transform( p );
172 if ( !rect.contains( mapPoint ) )
177 : mRenderContext.terrainRenderingEnabled() && mRenderContext.terrainGenerator()
178 ? static_cast<float>( mRenderContext.terrainGenerator()->heightAt( mapPoint.x(), mapPoint.y(), mRenderContext ) * mRenderContext.terrainSettings()->verticalScale() )
181 switch ( altitudeClamping )
190 z = terrainZ + zOffset;
194 TextBillboard billboard;
195 billboard.position = (
QgsVector3D( mapPoint.
x(), mapPoint.
y(), z ) - mChunkOrigin ).toVector3D();
196 billboard.text = text;
197 textBillboards.emplace_back( std::move( billboard ) );
198 textBillboardTexts.append( text );
202 mCalloutLines <<
QgsLineString( { mapPoint.
x(), mapPoint.
x() }, { mapPoint.
y(), mapPoint.
y() }, { terrainZ, z } );
205 mZMax = std::max( mZMax, showCallouts ? std::max( 0.0, z ) : z );
206 mZMin = std::min( mZMin, showCallouts ? std::min( 0.0, z ) : z );
215 for (
const std::unique_ptr< QgsAnnotationItem > &item : std::as_const( mItemsToRender ) )
227 if ( marker->symbol() )
232 const QgsPointXY mapPoint = layerToMapTransform.transform( p );
233 if ( !rect.contains( mapPoint ) )
238 : mRenderContext.terrainRenderingEnabled() && mRenderContext.terrainGenerator()
239 ? static_cast<float>( mRenderContext.terrainGenerator()->heightAt( mapPoint.x(), mapPoint.y(), mRenderContext ) * mRenderContext.terrainSettings()->verticalScale() )
242 switch ( altitudeClamping )
251 z = terrainZ + zOffset;
256 billboard.position = (
QgsVector3D( mapPoint.
x(), mapPoint.
y(), z ) - mChunkOrigin ).toVector3D();
257 billboard.textureId = -1;
259 for (
const Billboard &existingBillboard : billboards )
261 if ( existingBillboard.markerSymbol && marker->symbol()->rendersIdenticallyTo( existingBillboard.markerSymbol ) )
264 billboard.textureId = existingBillboard.textureId;
269 if ( billboard.textureId < 0 )
272 billboard.markerSymbol = marker->symbol();
273 billboard.textureId = textures.size();
274 textures.append( QgsPoint3DBillboardMaterial::renderSymbolToImage( marker->symbol(), mRenderContext ) );
276 billboards.emplace_back( std::move( billboard ) );
280 mCalloutLines <<
QgsLineString( { mapPoint.
x(), mapPoint.
x() }, { mapPoint.
y(), mapPoint.
y() }, { terrainZ, z } );
283 mZMax = std::max( mZMax, showCallouts ? std::max( 0.0, z ) : z );
284 mZMin = std::min( mZMin, showCallouts ? std::min( 0.0, z ) : z );
294 addTextBillboard( pointText->point(), pointText->text(), pointText->format() );
299 std::unique_ptr< QgsPoint > point(
geos.pointOnSurface() );
302 addTextBillboard( *point, lineText->text(), lineText->format() );
307 switch ( rectText->placementMode() )
312 addTextBillboard( rectText->bounds().center(), rectText->text(), rectText->format() );
322 mItemsToRender.clear();
324 if ( !textures.isEmpty() )
330 mBillboardPositions.reserve(
static_cast< int >( billboards.size() ) );
331 for ( Billboard &billboard : billboards )
333 const QRect textureRect = atlas.
rect( billboard.textureId );
335 geometry.
position = billboard.position;
337 static_cast< float >( textureRect.left() ) /
static_cast< float>( mBillboardAtlas.width() ),
338 1 - (
static_cast< float >( textureRect.bottom() ) /
static_cast< float>( mBillboardAtlas.height() ) )
341 static_cast< float >( textureRect.width() ) /
static_cast< float>( mBillboardAtlas.width() ),
static_cast< float>( textureRect.height() ) /
static_cast< float>( mBillboardAtlas.height() )
343 geometry.
pixelOffset = QPoint( 0, textureRect.height() / 2 );
344 mBillboardPositions.append( geometry );
349 QgsDebugError( u
"Error encountered building texture atlas"_s );
350 mBillboardAtlas = QImage();
355 mBillboardAtlas = QImage();
356 mBillboardPositions.clear();
360 if ( !textBillboardTexts.isEmpty() )
366 mTextBillboardPositions.reserve(
static_cast< int >( textBillboards.size() ) );
367 for ( TextBillboard &billboard : textBillboards )
369 int graphemeIndex = 0;
370 const int graphemeCount = atlas.
graphemeCount( billboard.text );
372 const double xOffset = atlas.
totalWidth( billboard.text ) / 2.0;
373 for ( ; graphemeIndex < graphemeCount; ++graphemeIndex )
377 geometry.
position = billboard.position;
379 static_cast< float >( textureRect.left() ) /
static_cast< float>( mTextBillboardAtlas.width() ),
380 1 - (
static_cast< float >( textureRect.bottom() ) /
static_cast< float>( mTextBillboardAtlas.height() ) )
383 static_cast< float >( textureRect.width() ) /
static_cast< float>( mTextBillboardAtlas.width() ),
384 static_cast< float>( textureRect.height() ) /
static_cast< float>( mTextBillboardAtlas.height() )
388 = QPoint(
static_cast< int >( std::round( -xOffset + pixelOffset.x() + 0.5 * textureRect.width() ) ),
static_cast< int >( std::round( pixelOffset.y() + 0.5 * textureRect.height() ) ) );
389 mTextBillboardPositions.append( geometry );
395 QgsDebugError( u
"Error encountered building font texture atlas"_s );
396 mTextBillboardAtlas = QImage();
401 mTextBillboardAtlas = QImage();
402 mTextBillboardPositions.clear();
407 mFutureWatcher->setFuture( future );
410QgsAnnotationLayerChunkLoader::~QgsAnnotationLayerChunkLoader()
412 if ( mFutureWatcher && !mFutureWatcher->isFinished() )
414 disconnect( mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsChunkQueueJob::finished );
415 mFutureWatcher->waitForFinished();
419void QgsAnnotationLayerChunkLoader::cancel()
424Qt3DCore::QEntity *QgsAnnotationLayerChunkLoader::createEntity( Qt3DCore::QEntity *parent )
426 if ( mNode->level() < mFactory->mLeafLevel )
428 Qt3DCore::QEntity *entity =
new Qt3DCore::QEntity( parent );
429 entity->setObjectName( mLayerName +
"_CONTAINER_" + mNode->tileId().text() );
433 if ( mBillboardPositions.empty() && mTextBillboardPositions.empty() )
438 mNode->updateParentBoundingBoxesRecursively();
442 Qt3DCore::QEntity *entity =
new Qt3DCore::QEntity( parent );
443 entity->setObjectName( mLayerName +
"_" + mNode->tileId().text() );
445 QgsGeoTransform *billboardTransform =
new QgsGeoTransform;
446 billboardTransform->setGeoTranslation( mChunkOrigin );
447 entity->addComponent( billboardTransform );
449 if ( !mBillboardPositions.empty() )
454 Qt3DRender::QGeometryRenderer *billboardGeometryRenderer =
new Qt3DRender::QGeometryRenderer;
455 billboardGeometryRenderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::Points );
456 billboardGeometryRenderer->setGeometry( billboardGeometry );
457 billboardGeometryRenderer->setVertexCount( billboardGeometry->
count() );
463 Qt3DCore::QEntity *billboardEntity =
new Qt3DCore::QEntity;
464 billboardEntity->addComponent( billboardMaterial );
465 billboardEntity->addComponent( billboardGeometryRenderer );
466 billboardEntity->setParent( entity );
469 if ( !mTextBillboardPositions.empty() )
474 Qt3DRender::QGeometryRenderer *billboardGeometryRenderer =
new Qt3DRender::QGeometryRenderer;
475 billboardGeometryRenderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::Points );
476 billboardGeometryRenderer->setGeometry( textBillboardGeometry );
477 billboardGeometryRenderer->setVertexCount( textBillboardGeometry->
count() );
482 Qt3DCore::QEntity *billboardEntity =
new Qt3DCore::QEntity;
483 billboardEntity->addComponent( billboardMaterial );
484 billboardEntity->addComponent( billboardGeometryRenderer );
485 billboardEntity->setParent( entity );
489 if ( mFactory->mShowCallouts )
491 QgsLineVertexData lineData;
492 lineData.withAdjacency =
true;
493 lineData.geocentricCoordinates =
false;
498 lineData.addLineString( line, 0,
false );
501 QgsLineMaterial *mat =
new QgsLineMaterial;
502 mat->setLineColor( mFactory->mCalloutLineColor );
503 mat->setLineWidth( mFactory->mCalloutLineWidth );
505 Qt3DCore::QEntity *calloutEntity =
new Qt3DCore::QEntity;
506 calloutEntity->setObjectName( parent->objectName() +
"_CALLOUTS" );
509 Qt3DRender::QGeometryRenderer *calloutRenderer =
new Qt3DRender::QGeometryRenderer;
510 calloutRenderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::LineStripAdjacency );
511 calloutRenderer->setGeometry( lineData.createGeometry( calloutEntity ) );
512 calloutRenderer->setVertexCount( lineData.indexes.count() );
513 calloutRenderer->setPrimitiveRestartEnabled(
true );
514 calloutRenderer->setRestartIndexValue( 0 );
517 calloutEntity->addComponent( calloutRenderer );
518 calloutEntity->addComponent( mat );
520 calloutEntity->setParent( entity );
524 if ( mZMin != std::numeric_limits<float>::max() && mZMax != std::numeric_limits<float>::lowest() )
529 mNode->setExactBox3D( box );
530 mNode->updateParentBoundingBoxesRecursively();
539QgsAnnotationLayerChunkLoaderFactory::QgsAnnotationLayerChunkLoaderFactory(
546 const QColor &calloutLineColor,
547 double calloutLineWidth,
552 : mRenderContext( context )
554 , mLeafLevel( leafLevel )
555 , mClamping( clamping )
556 , mZOffset( zOffset )
557 , mShowCallouts( showCallouts )
558 , mCalloutLineColor( calloutLineColor )
559 , mCalloutLineWidth( calloutLineWidth )
560 , mTextFormat( textFormat )
566 QgsDebugError( u
"Annotation layers in globe scenes are not supported yet!"_s );
567 setupQuadtree( QgsBox3D( -1e7, -1e7, -1e7, 1e7, 1e7, 1e7 ), -1, leafLevel );
573 rootBox3D.
grow( 1.0 );
574 setupQuadtree( rootBox3D, -1, leafLevel );
577QgsChunkLoader *QgsAnnotationLayerChunkLoaderFactory::createChunkLoader( QgsChunkNode *node )
const
579 return new QgsAnnotationLayerChunkLoader(
this, node );
586QgsAnnotationLayerChunkedEntity::QgsAnnotationLayerChunkedEntity(
592 const QColor &calloutLineColor,
593 double calloutLineWidth,
601 new QgsAnnotationLayerChunkLoaderFactory(
Qgs3DRenderContext::fromMapSettings( map ), layer, 3, clamping, zOffset, showCallouts, calloutLineColor, calloutLineWidth, textFormat, zMin, zMax ),
605 mTransform =
new Qt3DCore::QTransform;
606 if ( applyTerrainOffset() )
610 this->addComponent( mTransform );
615QgsAnnotationLayerChunkedEntity::~QgsAnnotationLayerChunkedEntity()
622bool QgsAnnotationLayerChunkedEntity::applyTerrainOffset()
const
624 if (
auto loaderFactory =
static_cast<QgsAnnotationLayerChunkLoaderFactory *
>( mChunkLoaderFactory ) )
631void QgsAnnotationLayerChunkedEntity::onTerrainElevationOffsetChanged()
633 QgsDebugMsgLevel( u
"QgsAnnotationLayerChunkedEntity::onTerrainElevationOffsetChanged"_s, 2 );
634 float newOffset =
static_cast<float>( qobject_cast<Qgs3DMapSettings *>( sender() )->terrainSettings()->elevationOffset() );
635 if ( !applyTerrainOffset() )
639 mTransform->setTranslation( QVector3D( 0.0f, 0.0f, newOffset ) );
AltitudeClamping
Altitude clamping.
@ Relative
Elevation is relative to terrain height (final elevation = terrain elevation + feature elevation).
@ Terrain
Elevation is clamped to terrain (final elevation = terrain elevation).
@ Absolute
Elevation is taken directly from feature and is independent of terrain height (final elevation = feat...
@ Geocentric
Geocentric CRS.
@ Vertex
Clamp every vertex of feature.
@ SpatialBounds
Item is rendered inside fixed spatial bounds, and size will depend on map scale.
@ FixedSize
Item is rendered at a fixed size, regardless of map scale. Item's location is georeferenced to a spat...
@ RelativeToMapFrame
Items size and placement is relative to the map's frame, and the item will always be rendered in the ...
@ Reverse
Reverse/inverse transform (from destination to source).
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.
void setZMinimum(double z)
Sets the minimum z value.
void setZMaximum(double z)
Sets the maximum z value.
void grow(double delta)
Grows the box in place by the specified amount in all dimensions.
Qgis::CrsType type() const
Returns the type of the CRS.
Custom exception class for Coordinate Reference System related exceptions.
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.
Line string geometry type, with support for z-dimension and m-values.
QgsCoordinateReferenceSystem crs
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.
A rectangle specified with double values.
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...
Contains geos related utilities and functions.
#define QgsDebugMsgLevel(str, level)
#define QgsDebugError(str)
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.