Skip to content

Commit

Permalink
Added support for background loading in 3D rule-based renderer
Browse files Browse the repository at this point in the history
  • Loading branch information
wonder-sk committed Jan 8, 2020
1 parent ab88777 commit e2863e3
Show file tree
Hide file tree
Showing 6 changed files with 429 additions and 32 deletions.
2 changes: 2 additions & 0 deletions src/3d/CMakeLists.txt
Expand Up @@ -17,6 +17,7 @@ SET(QGIS_3D_SRCS
qgspointlightsettings.cpp
qgsraycastingutils_p.cpp
qgsrulebased3drenderer.cpp
qgsrulebasedchunkloader_p.cpp
qgstessellatedpolygongeometry.cpp
qgstilingscheme.cpp
qgsvectorlayer3drenderer.cpp
Expand Down Expand Up @@ -101,6 +102,7 @@ SET(QGIS_3D_HDRS
)

SET(QGIS_3D_PRIVATE_HDRS
qgsrulebasedchunkloader_p.h
qgsvectorlayerchunkloader_p.h
chunks/qgschunkboundsentity_p.h
chunks/qgschunkedentity_p.h
Expand Down
35 changes: 3 additions & 32 deletions src/3d/qgsrulebased3drenderer.cpp
Expand Up @@ -27,6 +27,8 @@
#include "qgspoint3dsymbol_p.h"
#include "qgspolygon3dsymbol_p.h"

#include "qgsrulebasedchunkloader_p.h"

QgsRuleBased3DRendererMetadata::QgsRuleBased3DRendererMetadata()
: Qgs3DRendererAbstractMetadata( QStringLiteral( "rulebased" ) )
{
Expand Down Expand Up @@ -414,38 +416,7 @@ Qt3DCore::QEntity *QgsRuleBased3DRenderer::createEntity( const Qgs3DMapSettings
if ( !vl )
return nullptr;

Qgs3DRenderContext context( map );

QgsExpressionContext exprContext( Qgs3DUtils::globalProjectLayerExpressionContext( vl ) );
exprContext.setFields( vl->fields() );
context.setExpressionContext( exprContext );

RuleToHandlerMap handlers;
mRootRule->createHandlers( vl, handlers );

QSet<QString> attributeNames;
mRootRule->prepare( context, attributeNames, handlers );

QgsFeatureRequest req;
req.setDestinationCrs( map.crs(), map.transformContext() );
req.setSubsetOfAttributes( attributeNames, vl->fields() );

QgsFeature f;
QgsFeatureIterator fi = vl->getFeatures( req );
while ( fi.nextFeature( f ) )
{
context.expressionContext().setFeature( f );
mRootRule->registerFeature( f, context, handlers );
}

Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
for ( QgsFeature3DHandler *handler : handlers.values() )
handler->finalize( entity, context );

qDeleteAll( handlers );

return entity;

return new QgsRuleBasedChunkedEntity( vl, mRootRule, map );
}

void QgsRuleBased3DRenderer::writeXml( QDomElement &elem, const QgsReadWriteContext &context ) const
Expand Down
129 changes: 129 additions & 0 deletions src/3d/qgsrulebasedchunkloader_p.cpp
@@ -0,0 +1,129 @@
#include "qgsrulebasedchunkloader_p.h"

#include "qgs3dutils.h"
#include "qgschunknode_p.h"
#include "qgspolygon3dsymbol_p.h"
#include "qgseventtracing.h"

#include "qgsvectorlayer.h"
#include "qgsvectorlayerfeatureiterator.h"

#include "qgsrulebased3drenderer.h"

#include <QtConcurrent>


QgsRuleBasedChunkLoader::QgsRuleBasedChunkLoader( const QgsRuleBasedChunkLoaderFactory *factory, QgsChunkNode *node )
: QgsChunkLoader( node )
, mFactory( factory )
, mContext( factory->mMap )
{
if ( node->level() < mFactory->mLeafLevel )
{
QTimer::singleShot( 0, this, &QgsRuleBasedChunkLoader::finished );
return;
}

QgsVectorLayer *layer = mFactory->mLayer;
const Qgs3DMapSettings &map = mFactory->mMap;

QgsExpressionContext exprContext( Qgs3DUtils::globalProjectLayerExpressionContext( layer ) );
exprContext.setFields( layer->fields() );
mContext.setExpressionContext( exprContext );

mFactory->mRootRule->createHandlers( layer, mHandlers );

QSet<QString> attributeNames;
mFactory->mRootRule->prepare( mContext, attributeNames, mHandlers );

// build the feature request
QgsFeatureRequest req;
req.setDestinationCrs( map.crs(), map.transformContext() );
req.setSubsetOfAttributes( attributeNames, layer->fields() );

// only a subset of data to be queried
QgsRectangle rect = Qgs3DUtils::worldToMapExtent( node->bbox(), mFactory->mLayer->crs(), map.origin(), map.crs() );
req.setFilterRect( rect );

//
// this will be run in a background thread
//

QFuture<void> future = QtConcurrent::run( [req, this]
{
QgsEventTracing::ScopedEvent e( "3D", "RB chunk load" );

QgsFeature f;
QgsFeatureIterator fi = mFactory->mSource->getFeatures( req );
while ( fi.nextFeature( f ) )
{
if ( mCanceled )
break;
mContext.expressionContext().setFeature( f );
mFactory->mRootRule->registerFeature( f, mContext, mHandlers );
}
} );

// emit finished() as soon as the handler is populated with features
QFutureWatcher<void> *fw = new QFutureWatcher<void>( nullptr );
fw->setFuture( future );
connect( fw, &QFutureWatcher<void>::finished, this, &QgsChunkQueueJob::finished );
}

void QgsRuleBasedChunkLoader::cancel()
{
mCanceled = true;
}

Qt3DCore::QEntity *QgsRuleBasedChunkLoader::createEntity( Qt3DCore::QEntity *parent )
{
if ( mNode->level() < mFactory->mLeafLevel )
{
return new Qt3DCore::QEntity( parent ); // dummy entity
}

Qt3DCore::QEntity *entity = new Qt3DCore::QEntity( parent );
for ( QgsFeature3DHandler *handler : mHandlers.values() )
handler->finalize( entity, mContext );

qDeleteAll( mHandlers );
mHandlers.clear();

return entity;
}


///////////////


QgsRuleBasedChunkLoaderFactory::QgsRuleBasedChunkLoaderFactory( const Qgs3DMapSettings &map, QgsVectorLayer *vl, QgsRuleBased3DRenderer::Rule *rootRule, int leafLevel )
: mMap( map )
, mLayer( vl )
, mRootRule( rootRule->clone() )
, mLeafLevel( leafLevel )
, mSource( new QgsVectorLayerFeatureSource( vl ) )
{
}

QgsChunkLoader *QgsRuleBasedChunkLoaderFactory::createChunkLoader( QgsChunkNode *node ) const
{
return new QgsRuleBasedChunkLoader( this, node );
}


///////////////

QgsRuleBasedChunkedEntity::QgsRuleBasedChunkedEntity( QgsVectorLayer *vl, QgsRuleBased3DRenderer::Rule *rootRule, const Qgs3DMapSettings &map )
: QgsChunkedEntity( Qgs3DUtils::mapToWorldExtent( vl->extent(), vl->crs(), map.origin(), map.crs() ),
-1, // rootError TODO: negative error should mean that the node does not contain anything
-1, // tau = max. allowed screen error. TODO: negative tau should mean that we need to go until leaves are reached
2, // TODO: figure out from the number of features
new QgsRuleBasedChunkLoaderFactory( map, vl, rootRule, 2 ) )
{
}

QgsRuleBasedChunkedEntity::~QgsRuleBasedChunkedEntity()
{
// cancel / wait for jobs
cancelActiveJobs();
}
68 changes: 68 additions & 0 deletions src/3d/qgsrulebasedchunkloader_p.h
@@ -0,0 +1,68 @@
#ifndef QGSRULEBASEDCHUNKLOADER_H
#define QGSRULEBASEDCHUNKLOADER_H

#include "qgschunkloader_p.h"
#include "qgsfeature3dhandler_p.h"

class Qgs3DMapSettings;
class QgsVectorLayer;
class QgsVectorLayerFeatureSource;
class QgsAbstract3DSymbol;
class QgsFeature3DHandler;

#include "qgsrulebased3drenderer.h"


class QgsRuleBasedChunkLoaderFactory : public QgsChunkLoaderFactory
{
public:
QgsRuleBasedChunkLoaderFactory( const Qgs3DMapSettings &map, QgsVectorLayer *vl, QgsRuleBased3DRenderer::Rule *rootRule, int leafLevel );

//! Creates loader for the given chunk node. Ownership of the returned is passed to the caller.
virtual QgsChunkLoader *createChunkLoader( QgsChunkNode *node ) const;

const Qgs3DMapSettings &mMap;
QgsVectorLayer *mLayer;
std::unique_ptr<QgsRuleBased3DRenderer::Rule> mRootRule;
int mLeafLevel;
std::unique_ptr<QgsVectorLayerFeatureSource> mSource;
};




class QgsRuleBasedChunkLoader : public QgsChunkLoader
{
public:
QgsRuleBasedChunkLoader( const QgsRuleBasedChunkLoaderFactory *factory, QgsChunkNode *node );

virtual void cancel();
virtual Qt3DCore::QEntity *createEntity( Qt3DCore::QEntity *parent );

private:
const QgsRuleBasedChunkLoaderFactory *mFactory;
QgsRuleBased3DRenderer::RuleToHandlerMap mHandlers;
Qgs3DRenderContext mContext;
bool mCanceled = false;
};



#include "qgschunkedentity_p.h"

class QgsRuleBasedChunkedEntity : public QgsChunkedEntity
{
Q_OBJECT
public:

// TODO: should use a clone of root rule?

//! Constructs the entity. The argument maxLevel determines how deep the tree of tiles will be
explicit QgsRuleBasedChunkedEntity( QgsVectorLayer *vl, QgsRuleBased3DRenderer::Rule *rootRule, const Qgs3DMapSettings &map );

~QgsRuleBasedChunkedEntity();
};



#endif // QGSRULEBASEDCHUNKLOADER_H

0 comments on commit e2863e3

Please sign in to comment.