1 /***************************************************************************
2 qgsvectorlayerchunkloader_p.cpp
3 --------------------------------------
4 Date : July 2019
5 Copyright : (C) 2019 by Martin Dobias
6 Email : wonder dot sk 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
16 #include "qgsvectorlayerchunkloader_p.h"
17
18 #include "qgs3dutils.h"
19 #include "qgsabstractvectorlayer3drenderer.h"
20 #include "qgschunknode_p.h"
21 #include "qgspolygon3dsymbol_p.h"
22 #include "qgseventtracing.h"
23 #include "qgslogger.h"
24 #include "qgsvectorlayer.h"
25 #include "qgsvectorlayerfeatureiterator.h"
26
27 #include "qgsline3dsymbol.h"
28 #include "qgspoint3dsymbol.h"
29 #include "qgspolygon3dsymbol.h"
30
31 #include "qgsapplication.h"
32 #include "qgs3dsymbolregistry.h"
33
34 #include <QtConcurrent>
35
36 ///@cond PRIVATE
37
38
QgsVectorLayerChunkLoader(const QgsVectorLayerChunkLoaderFactory * factory,QgsChunkNode * node)39 QgsVectorLayerChunkLoader::QgsVectorLayerChunkLoader( const QgsVectorLayerChunkLoaderFactory *factory, QgsChunkNode *node )
40 : QgsChunkLoader( node )
41 , mFactory( factory )
42 , mContext( factory->mMap )
43 , mSource( new QgsVectorLayerFeatureSource( factory->mLayer ) )
44 {
45 if ( node->level() < mFactory->mLeafLevel )
46 {
47 QTimer::singleShot( 0, this, &QgsVectorLayerChunkLoader::finished );
48 return;
49 }
50
51 QgsVectorLayer *layer = mFactory->mLayer;
52 const Qgs3DMapSettings &map = mFactory->mMap;
53
54 QgsFeature3DHandler *handler = QgsApplication::symbol3DRegistry()->createHandlerForSymbol( layer, mFactory->mSymbol.get() );
55 if ( !handler )
56 {
57 QgsDebugMsg( QStringLiteral( "Unknown 3D symbol type for vector layer: " ) + mFactory->mSymbol->type() );
58 return;
59 }
60 mHandler.reset( handler );
61
62 QgsExpressionContext exprContext( Qgs3DUtils::globalProjectLayerExpressionContext( layer ) );
63 exprContext.setFields( layer->fields() );
64 mContext.setExpressionContext( exprContext );
65
66 QSet<QString> attributeNames;
67 if ( !mHandler->prepare( mContext, attributeNames ) )
68 {
69 QgsDebugMsg( QStringLiteral( "Failed to prepare 3D feature handler!" ) );
70 return;
71 }
72
73 // build the feature request
74 QgsFeatureRequest req;
75 req.setDestinationCrs( map.crs(), map.transformContext() );
76 req.setSubsetOfAttributes( attributeNames, layer->fields() );
77
78 // only a subset of data to be queried
79 QgsRectangle rect = Qgs3DUtils::worldToMapExtent( node->bbox(), map.origin() );
80 req.setFilterRect( rect );
81
82 //
83 // this will be run in a background thread
84 //
85
86 QFuture<void> future = QtConcurrent::run( [req, this]
87 {
88 QgsEventTracing::ScopedEvent e( QStringLiteral( "3D" ), QStringLiteral( "VL chunk load" ) );
89
90 QgsFeature f;
91 QgsFeatureIterator fi = mSource->getFeatures( req );
92 while ( fi.nextFeature( f ) )
93 {
94 if ( mCanceled )
95 break;
96 mContext.expressionContext().setFeature( f );
97 mHandler->processFeature( f, mContext );
98 }
99 } );
100
101 // emit finished() as soon as the handler is populated with features
102 mFutureWatcher = new QFutureWatcher<void>( this );
103 mFutureWatcher->setFuture( future );
104 connect( mFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsChunkQueueJob::finished );
105 }
106
~QgsVectorLayerChunkLoader()107 QgsVectorLayerChunkLoader::~QgsVectorLayerChunkLoader()
108 {
109 if ( mFutureWatcher && !mFutureWatcher->isFinished() )
110 {
111 disconnect( mFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsChunkQueueJob::finished );
112 mFutureWatcher->waitForFinished();
113 }
114 }
115
cancel()116 void QgsVectorLayerChunkLoader::cancel()
117 {
118 mCanceled = true;
119 }
120
createEntity(Qt3DCore::QEntity * parent)121 Qt3DCore::QEntity *QgsVectorLayerChunkLoader::createEntity( Qt3DCore::QEntity *parent )
122 {
123 if ( mNode->level() < mFactory->mLeafLevel )
124 {
125 return new Qt3DCore::QEntity( parent ); // dummy entity
126 }
127
128 Qt3DCore::QEntity *entity = new Qt3DCore::QEntity( parent );
129 mHandler->finalize( entity, mContext );
130 return entity;
131 }
132
133
134 ///////////////
135
136
QgsVectorLayerChunkLoaderFactory(const Qgs3DMapSettings & map,QgsVectorLayer * vl,QgsAbstract3DSymbol * symbol,int leafLevel)137 QgsVectorLayerChunkLoaderFactory::QgsVectorLayerChunkLoaderFactory( const Qgs3DMapSettings &map, QgsVectorLayer *vl, QgsAbstract3DSymbol *symbol, int leafLevel )
138 : mMap( map )
139 , mLayer( vl )
140 , mSymbol( symbol->clone() )
141 , mLeafLevel( leafLevel )
142 {
143 }
144
createChunkLoader(QgsChunkNode * node) const145 QgsChunkLoader *QgsVectorLayerChunkLoaderFactory::createChunkLoader( QgsChunkNode *node ) const
146 {
147 return new QgsVectorLayerChunkLoader( this, node );
148 }
149
150
151 ///////////////
152
153
QgsVectorLayerChunkedEntity(QgsVectorLayer * vl,double zMin,double zMax,const QgsVectorLayer3DTilingSettings & tilingSettings,QgsAbstract3DSymbol * symbol,const Qgs3DMapSettings & map)154 QgsVectorLayerChunkedEntity::QgsVectorLayerChunkedEntity( QgsVectorLayer *vl, double zMin, double zMax, const QgsVectorLayer3DTilingSettings &tilingSettings, QgsAbstract3DSymbol *symbol, const Qgs3DMapSettings &map )
155 : QgsChunkedEntity( Qgs3DUtils::layerToWorldExtent( vl->extent(), zMin, zMax, vl->crs(), map.origin(), map.crs(), map.transformContext() ),
156 -1, // rootError (negative error means that the node does not contain anything)
157 -1, // max. allowed screen error (negative tau means that we need to go until leaves are reached)
158 tilingSettings.zoomLevelsCount() - 1,
159 new QgsVectorLayerChunkLoaderFactory( map, vl, symbol, tilingSettings.zoomLevelsCount() - 1 ), true )
160 {
161 setShowBoundingBoxes( tilingSettings.showBoundingBoxes() );
162 }
163
~QgsVectorLayerChunkedEntity()164 QgsVectorLayerChunkedEntity::~QgsVectorLayerChunkedEntity()
165 {
166 // cancel / wait for jobs
167 cancelActiveJobs();
168 }
169
170 /// @endcond
171