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