diff --git a/python/core/auto_generated/pointcloud/qgspointcloudrendererregistry.sip.in b/python/core/auto_generated/pointcloud/qgspointcloudrendererregistry.sip.in new file mode 100644 index 000000000000..40fec60e6b3d --- /dev/null +++ b/python/core/auto_generated/pointcloud/qgspointcloudrendererregistry.sip.in @@ -0,0 +1,165 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/pointcloud/qgspointcloudrendererregistry.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + +class QgsPointCloudRendererAbstractMetadata +{ +%Docstring +Stores metadata about one point cloud renderer class. + +.. note:: + + It's necessary to implement :py:func:`~createRenderer` function. + In C++ you can use QgsPointCloudRendererMetadata convenience class. + +.. versionadded:: 3.18 +%End + +%TypeHeaderCode +#include "qgspointcloudrendererregistry.h" +%End + public: + + QgsPointCloudRendererAbstractMetadata( const QString &name, const QString &visibleName, const QIcon &icon = QIcon() ); +%Docstring +Constructor for QgsPointCloudRendererAbstractMetadata, with the specified ``name``. + +The ``visibleName`` argument gives a translated, user friendly string identifying the renderer type. + +The ``icon`` argument can be used to specify an icon representing the renderer. +%End + virtual ~QgsPointCloudRendererAbstractMetadata(); + + QString name() const; +%Docstring +Returns the unique name of the renderer. This value is not translated. + +.. seealso:: :py:func:`visibleName` +%End + + QString visibleName() const; +%Docstring +Returns a friendly display name of the renderer. This value is translated. + +.. seealso:: :py:func:`name` +%End + + QIcon icon() const; +%Docstring +Returns an icon representing the renderer. + +.. seealso:: :py:func:`setIcon` +%End + + void setIcon( const QIcon &icon ); +%Docstring +Sets an ``icon`` representing the renderer. + +.. seealso:: :py:func:`icon` +%End + + virtual QgsPointCloudRenderer *createRenderer( QDomElement &elem, const QgsReadWriteContext &context ) = 0 /Factory/; +%Docstring +Returns new instance of the renderer given the DOM element. Returns ``None`` on error. +Pure virtual function: must be implemented in derived classes. +%End + + + protected: +}; + + +class QgsPointCloudRendererMetadata : QgsPointCloudRendererAbstractMetadata +{ +%Docstring +Convenience metadata class that uses static functions to create point cloud renderer and its widget. + +.. versionadded:: 3.18 +%End + +%TypeHeaderCode +#include "qgspointcloudrendererregistry.h" +%End + public: + + + virtual QgsPointCloudRenderer *createRenderer( QDomElement &elem, const QgsReadWriteContext &context ) /Factory/; + + + + + protected: + + private: + QgsPointCloudRendererMetadata(); +}; + + +class QgsPointCloudRendererRegistry +{ +%Docstring +Registry of 2D renderers for point clouds. + +QgsPointCloudRendererRegistry is not usually directly created, but rather accessed through +:py:func:`QgsApplication.pointCloudRendererRegistry()`. + +.. versionadded:: 3.18 +%End + +%TypeHeaderCode +#include "qgspointcloudrendererregistry.h" +%End + public: + + QgsPointCloudRendererRegistry(); + ~QgsPointCloudRendererRegistry(); + + + bool addRenderer( QgsPointCloudRendererAbstractMetadata *metadata /Transfer/ ); +%Docstring +Adds a renderer to the registry. Takes ownership of the metadata object. + +:param metadata: renderer metadata + +:return: ``True`` if renderer was added successfully, or ``False`` if renderer could not + be added (e.g., a renderer with a duplicate name already exists) +%End + + bool removeRenderer( const QString &rendererName ); +%Docstring +Removes a renderer from registry. + +:param rendererName: name of renderer to remove from registry + +:return: ``True`` if renderer was successfully removed, or ``False`` if matching + renderer could not be found +%End + + QgsPointCloudRendererAbstractMetadata *rendererMetadata( const QString &rendererName ); +%Docstring +Returns the metadata for a specified renderer. Returns ``None`` if a matching +renderer was not found in the registry. +%End + + QStringList renderersList() const; +%Docstring +Returns a list of available renderers. +%End + + private: + QgsPointCloudRendererRegistry( const QgsPointCloudRendererRegistry &rh ); +}; + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/pointcloud/qgspointcloudrendererregistry.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/python/core/auto_generated/qgsapplication.sip.in b/python/core/auto_generated/qgsapplication.sip.in index 938692719f1b..1df89d54fc41 100644 --- a/python/core/auto_generated/qgsapplication.sip.in +++ b/python/core/auto_generated/qgsapplication.sip.in @@ -674,6 +674,13 @@ Returns the application's renderer registry, used for managing vector layer rend %End + static QgsPointCloudRendererRegistry *pointCloudRendererRegistry() /KeepReference/; +%Docstring +Returns the application's point cloud renderer registry, used for managing point cloud layer 2D renderers. + +.. versionadded:: 3.18 +%End + static QgsDataItemProviderRegistry *dataItemProviderRegistry() /KeepReference/; %Docstring Returns the application's data item provider registry, which keeps a list of data item diff --git a/python/core/core_auto.sip b/python/core/core_auto.sip index dfd47d73aab3..24799f1d8248 100644 --- a/python/core/core_auto.sip +++ b/python/core/core_auto.sip @@ -449,6 +449,7 @@ %Include auto_generated/pointcloud/qgspointcloudlayer.sip %Include auto_generated/pointcloud/qgspointclouddataprovider.sip %Include auto_generated/pointcloud/qgspointcloudrenderer.sip +%Include auto_generated/pointcloud/qgspointcloudrendererregistry.sip %Include auto_generated/metadata/qgsabstractmetadatabase.sip %Include auto_generated/metadata/qgslayermetadata.sip %Include auto_generated/metadata/qgslayermetadataformatter.sip diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 7759b87e7b24..3e7e827140bd 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -639,6 +639,7 @@ set(QGIS_CORE_SRCS pointcloud/qgspointcloudindex.cpp pointcloud/qgspointclouddataprovider.cpp pointcloud/qgspointcloudrenderer.cpp + pointcloud/qgspointcloudrendererregistry.cpp labeling/qgslabelfeature.cpp labeling/qgslabelingengine.cpp @@ -1325,6 +1326,7 @@ set(QGIS_CORE_HDRS pointcloud/qgspointcloudindex.h pointcloud/qgspointclouddataprovider.h pointcloud/qgspointcloudrenderer.h + pointcloud/qgspointcloudrendererregistry.h metadata/qgsabstractmetadatabase.h metadata/qgslayermetadata.h diff --git a/src/core/pointcloud/qgspointcloudrenderer.cpp b/src/core/pointcloud/qgspointcloudrenderer.cpp index 233f3a9e49c4..c3ac252d6cbc 100644 --- a/src/core/pointcloud/qgspointcloudrenderer.cpp +++ b/src/core/pointcloud/qgspointcloudrenderer.cpp @@ -16,6 +16,8 @@ ***************************************************************************/ #include "qgspointcloudrenderer.h" +#include "qgspointcloudrendererregistry.h" +#include "qgsapplication.h" QgsPointCloudRenderContext::QgsPointCloudRenderContext( QgsRenderContext &context, const QgsVector3D &scale, const QgsVector3D &offset ) : mRenderContext( context ) @@ -46,17 +48,14 @@ QgsPointCloudRenderer *QgsPointCloudRenderer::load( QDomElement &element, const return nullptr; // load renderer - QString rendererType = element.attribute( QStringLiteral( "type" ) ); + const QString rendererType = element.attribute( QStringLiteral( "type" ) ); -#if 0 - QgsRendererAbstractMetadata *m = QgsApplication::rendererRegistry()->rendererMetadata( rendererType ); + QgsPointCloudRendererAbstractMetadata *m = QgsApplication::pointCloudRendererRegistry()->rendererMetadata( rendererType ); if ( !m ) return nullptr; std::unique_ptr< QgsPointCloudRenderer > r( m->createRenderer( element, context ) ); return r.release(); -#endif - return QgsDummyPointCloudRenderer::create( element, context ); } QSet QgsPointCloudRenderer::usedAttributes( const QgsPointCloudRenderContext & ) const diff --git a/src/core/pointcloud/qgspointcloudrendererregistry.cpp b/src/core/pointcloud/qgspointcloudrendererregistry.cpp new file mode 100644 index 000000000000..e81bff5b5b19 --- /dev/null +++ b/src/core/pointcloud/qgspointcloudrendererregistry.cpp @@ -0,0 +1,72 @@ +/*************************************************************************** + qgspointcloudrendererregistry.cpp + --------------------- + begin : November 2020 + copyright : (C) 2020 by Nyall Dawson + email : nyall dot dawson at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#include "qgspointcloudrendererregistry.h" +#include "qgspointcloudrenderer.h" + +// default renderers + +#include "qgspointcloudlayer.h" + +QgsPointCloudRendererRegistry::QgsPointCloudRendererRegistry() +{ + // add default renderers + addRenderer( new QgsPointCloudRendererMetadata( QStringLiteral( "dummy" ), + QObject::tr( "Dummy" ), + QgsDummyPointCloudRenderer::create ) ); +} + +QgsPointCloudRendererRegistry::~QgsPointCloudRendererRegistry() +{ + qDeleteAll( mRenderers ); +} + +bool QgsPointCloudRendererRegistry::addRenderer( QgsPointCloudRendererAbstractMetadata *metadata ) +{ + if ( !metadata || mRenderers.contains( metadata->name() ) ) + return false; + + mRenderers[metadata->name()] = metadata; + mRenderersOrder << metadata->name(); + return true; +} + +bool QgsPointCloudRendererRegistry::removeRenderer( const QString &rendererName ) +{ + if ( !mRenderers.contains( rendererName ) ) + return false; + + delete mRenderers[rendererName]; + mRenderers.remove( rendererName ); + mRenderersOrder.removeAll( rendererName ); + return true; +} + +QgsPointCloudRendererAbstractMetadata *QgsPointCloudRendererRegistry::rendererMetadata( const QString &rendererName ) +{ + return mRenderers.value( rendererName ); +} + +QStringList QgsPointCloudRendererRegistry::renderersList() const +{ + QStringList renderers; + for ( const QString &renderer : mRenderersOrder ) + { + QgsPointCloudRendererAbstractMetadata *r = mRenderers.value( renderer ); + if ( r ) + renderers << renderer; + } + return renderers; +} + diff --git a/src/core/pointcloud/qgspointcloudrendererregistry.h b/src/core/pointcloud/qgspointcloudrendererregistry.h new file mode 100644 index 000000000000..dbc099289faa --- /dev/null +++ b/src/core/pointcloud/qgspointcloudrendererregistry.h @@ -0,0 +1,231 @@ +/*************************************************************************** + qgspointcloudrendererregistry.h + --------------------- + begin : November 2020 + copyright : (C) 2020 by Nyall Dawson + email : nyall dot dawson at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#ifndef QGSPOINTCLOUDRENDERERREGISTRY_H +#define QGSPOINTCLOUDRENDERERREGISTRY_H + +#include "qgis_core.h" +#include "qgis_sip.h" +#include +#include +#include +#include + +class QgsPointCloudRenderer; +class QgsReadWriteContext; +class QgsPointCloudLayer; +class QgsStyle; +#ifndef SIP_RUN +class QgsPointCloudRendererWidget SIP_EXTERNAL; +#endif + +/** + * \ingroup core + * Stores metadata about one point cloud renderer class. + * + * \note It's necessary to implement createRenderer() function. + * In C++ you can use QgsPointCloudRendererMetadata convenience class. + * + * \since QGIS 3.18 + */ +class CORE_EXPORT QgsPointCloudRendererAbstractMetadata +{ + public: + + /** + * Constructor for QgsPointCloudRendererAbstractMetadata, with the specified \a name. + * + * The \a visibleName argument gives a translated, user friendly string identifying the renderer type. + * + * The \a icon argument can be used to specify an icon representing the renderer. + */ + QgsPointCloudRendererAbstractMetadata( const QString &name, const QString &visibleName, const QIcon &icon = QIcon() ) + : mName( name ) + , mVisibleName( visibleName ) + , mIcon( icon ) + {} + virtual ~QgsPointCloudRendererAbstractMetadata() = default; + + /** + * Returns the unique name of the renderer. This value is not translated. + * \see visibleName() + */ + QString name() const { return mName; } + + /** + * Returns a friendly display name of the renderer. This value is translated. + * \see name() + */ + QString visibleName() const { return mVisibleName; } + + /** + * Returns an icon representing the renderer. + * \see setIcon() + */ + QIcon icon() const { return mIcon; } + + /** + * Sets an \a icon representing the renderer. + * \see icon() + */ + void setIcon( const QIcon &icon ) { mIcon = icon; } + + /** + * Returns new instance of the renderer given the DOM element. Returns NULLPTR on error. + * Pure virtual function: must be implemented in derived classes. + */ + virtual QgsPointCloudRenderer *createRenderer( QDomElement &elem, const QgsReadWriteContext &context ) = 0 SIP_FACTORY; + +#ifndef SIP_RUN + + /** + * Returns new instance of settings widget for the renderer. Returns NULLPTR on error. + * + * The \a oldRenderer argument may refer to previously used renderer (or it is NULLPTR). + * If not NULLPTR, it may be used to initialize GUI of the widget from the previous settings. + * The old renderer does not have to be of the same type as returned by createRenderer(). + * + * \note Not available in Python bindings + */ + virtual QgsPointCloudRendererWidget *createRendererWidget( QgsPointCloudLayer *layer, QgsStyle *style, QgsPointCloudRenderer *oldRenderer ) SIP_FACTORY + { Q_UNUSED( layer ) Q_UNUSED( style ); Q_UNUSED( oldRenderer ); return nullptr; } +#endif + + protected: + //! name used within QGIS for identification (the same what renderer's type() returns) + QString mName; + //! name visible for users (translatable) + QString mVisibleName; + //! icon to be shown in the renderer properties dialog + QIcon mIcon; +}; + +typedef QgsPointCloudRenderer *( *QgsPointCloudRendererCreateFunc )( QDomElement &, const QgsReadWriteContext & ) SIP_SKIP; +typedef QgsPointCloudRendererWidget *( *QgsPointCloudRendererWidgetFunc )( QgsPointCloudLayer *, QgsStyle *, QgsPointCloudRenderer * ) SIP_SKIP; + +/** + * \ingroup core + * Convenience metadata class that uses static functions to create point cloud renderer and its widget. + * \since QGIS 3.18 + */ +class CORE_EXPORT QgsPointCloudRendererMetadata : public QgsPointCloudRendererAbstractMetadata +{ + public: + + /** + * Construct metadata + * \note not available in Python bindings + */ + QgsPointCloudRendererMetadata( const QString &name, + const QString &visibleName, + QgsPointCloudRendererCreateFunc pfCreate, + const QIcon &icon = QIcon(), + QgsPointCloudRendererWidgetFunc pfWidget = nullptr ) SIP_SKIP + : QgsPointCloudRendererAbstractMetadata( name, visibleName, icon ) + , mCreateFunc( pfCreate ) + , mWidgetFunc( pfWidget ) + {} + + QgsPointCloudRenderer *createRenderer( QDomElement &elem, const QgsReadWriteContext &context ) override SIP_FACTORY + { return mCreateFunc ? mCreateFunc( elem, context ) : nullptr; } + +#ifndef SIP_RUN + QgsPointCloudRendererWidget *createRendererWidget( QgsPointCloudLayer *layer, QgsStyle *style, QgsPointCloudRenderer *renderer ) override SIP_FACTORY + { return mWidgetFunc ? mWidgetFunc( layer, style, renderer ) : nullptr; } +#endif + + //! \note not available in Python bindings + QgsPointCloudRendererCreateFunc createFunction() const { return mCreateFunc; } SIP_SKIP + //! \note not available in Python bindings + QgsPointCloudRendererWidgetFunc widgetFunction() const { return mWidgetFunc; } SIP_SKIP + + //! \note not available in Python bindings + void setWidgetFunction( QgsPointCloudRendererWidgetFunc f ) { mWidgetFunc = f; } SIP_SKIP + + protected: + //! pointer to function that creates an instance of the renderer when loading project / style + QgsPointCloudRendererCreateFunc mCreateFunc; + //! pointer to function that creates a widget for configuration of renderer's params + QgsPointCloudRendererWidgetFunc mWidgetFunc; + + private: +#ifdef SIP_RUN + QgsPointCloudRendererMetadata(); +#endif + +}; + + +/** + * \ingroup core + * \class QgsPointCloudRendererRegistry + * \brief Registry of 2D renderers for point clouds. + * + * QgsPointCloudRendererRegistry is not usually directly created, but rather accessed through + * QgsApplication::pointCloudRendererRegistry(). + * + * \since QGIS 3.18 + */ +class CORE_EXPORT QgsPointCloudRendererRegistry +{ + public: + + QgsPointCloudRendererRegistry(); + ~QgsPointCloudRendererRegistry(); + + //! QgsPointCloudRendererRegistry cannot be copied. + QgsPointCloudRendererRegistry( const QgsPointCloudRendererRegistry &rh ) = delete; + //! QgsPointCloudRendererRegistry cannot be copied. + QgsPointCloudRendererRegistry &operator=( const QgsPointCloudRendererRegistry &rh ) = delete; + + /** + * Adds a renderer to the registry. Takes ownership of the metadata object. + * \param metadata renderer metadata + * \returns TRUE if renderer was added successfully, or FALSE if renderer could not + * be added (e.g., a renderer with a duplicate name already exists) + */ + bool addRenderer( QgsPointCloudRendererAbstractMetadata *metadata SIP_TRANSFER ); + + /** + * Removes a renderer from registry. + * \param rendererName name of renderer to remove from registry + * \returns TRUE if renderer was successfully removed, or FALSE if matching + * renderer could not be found + */ + bool removeRenderer( const QString &rendererName ); + + /** + * Returns the metadata for a specified renderer. Returns NULLPTR if a matching + * renderer was not found in the registry. + */ + QgsPointCloudRendererAbstractMetadata *rendererMetadata( const QString &rendererName ); + + /** + * Returns a list of available renderers. + */ + QStringList renderersList() const; + + private: +#ifdef SIP_RUN + QgsPointCloudRendererRegistry( const QgsPointCloudRendererRegistry &rh ); +#endif + + //! Map of name to renderer + QMap mRenderers; + + //! List of renderers, maintained in the order that they have been added + QStringList mRenderersOrder; +}; + +#endif // QGSPOINTCLOUDRENDERERREGISTRY_H diff --git a/src/core/qgsapplication.cpp b/src/core/qgsapplication.cpp index 43d2dc84cce7..89a0d803c43c 100644 --- a/src/core/qgsapplication.cpp +++ b/src/core/qgsapplication.cpp @@ -42,6 +42,7 @@ #include "qgsprojectstorageregistry.h" #include "qgsrasterrendererregistry.h" #include "qgsrendererregistry.h" +#include "qgspointcloudrendererregistry.h" #include "qgssymbollayerregistry.h" #include "qgssymbollayerutils.h" #include "qgscalloutsregistry.h" @@ -2157,6 +2158,11 @@ QgsRasterRendererRegistry *QgsApplication::rasterRendererRegistry() return members()->mRasterRendererRegistry; } +QgsPointCloudRendererRegistry *QgsApplication::pointCloudRendererRegistry() +{ + return members()->mPointCloudRendererRegistry; +} + QgsDataItemProviderRegistry *QgsApplication::dataItemProviderRegistry() { if ( auto *lInstance = instance() ) @@ -2385,6 +2391,11 @@ QgsApplication::ApplicationMembers::ApplicationMembers() mRasterRendererRegistry = new QgsRasterRendererRegistry(); profiler->end(); } + { + profiler->start( tr( "Setup point cloud renderer registry" ) ); + mPointCloudRendererRegistry = new QgsPointCloudRendererRegistry(); + profiler->end(); + } { profiler->start( tr( "Setup GPS registry" ) ); mGpsConnectionRegistry = new QgsGpsConnectionRegistry(); @@ -2480,6 +2491,7 @@ QgsApplication::ApplicationMembers::~ApplicationMembers() delete mPageSizeRegistry; delete mAnnotationItemRegistry; delete mLayoutItemRegistry; + delete mPointCloudRendererRegistry; delete mRasterRendererRegistry; delete mRendererRegistry; delete mSvgCache; diff --git a/src/core/qgsapplication.h b/src/core/qgsapplication.h index 0597c19cff9f..d999bc1c65af 100644 --- a/src/core/qgsapplication.h +++ b/src/core/qgsapplication.h @@ -63,6 +63,7 @@ class QgsNumericFormatRegistry; class QgsConnectionRegistry; class QgsScaleBarRendererRegistry; class Qgs3DSymbolRegistry; +class QgsPointCloudRendererRegistry; /** * \ingroup core @@ -632,6 +633,12 @@ class CORE_EXPORT QgsApplication : public QApplication */ static QgsRasterRendererRegistry *rasterRendererRegistry() SIP_SKIP; + /** + * Returns the application's point cloud renderer registry, used for managing point cloud layer 2D renderers. + * \since QGIS 3.18 + */ + static QgsPointCloudRendererRegistry *pointCloudRendererRegistry() SIP_KEEPREFERENCE; + /** * Returns the application's data item provider registry, which keeps a list of data item * providers that may add items to the browser tree. @@ -985,6 +992,7 @@ class CORE_EXPORT QgsApplication : public QApplication QgsPageSizeRegistry *mPageSizeRegistry = nullptr; QgsRasterRendererRegistry *mRasterRendererRegistry = nullptr; QgsRendererRegistry *mRendererRegistry = nullptr; + QgsPointCloudRendererRegistry *mPointCloudRendererRegistry = nullptr; QgsSvgCache *mSvgCache = nullptr; QgsImageCache *mImageCache = nullptr; QgsSourceCache *mSourceCache = nullptr; diff --git a/tests/src/core/CMakeLists.txt b/tests/src/core/CMakeLists.txt index e03f1f93c9b3..6d0d2817d5ee 100644 --- a/tests/src/core/CMakeLists.txt +++ b/tests/src/core/CMakeLists.txt @@ -203,6 +203,7 @@ set(TESTS testqgspointpatternfillsymbol.cpp testqgspoint.cpp testqgspointcloudattribute.cpp + testqgspointcloudrendererregistry.cpp testqgsproject.cpp testqgsprojectstorage.cpp testqgsprojutils.cpp diff --git a/tests/src/core/testqgspointcloudrendererregistry.cpp b/tests/src/core/testqgspointcloudrendererregistry.cpp new file mode 100644 index 000000000000..7cd18ecf4fc5 --- /dev/null +++ b/tests/src/core/testqgspointcloudrendererregistry.cpp @@ -0,0 +1,141 @@ +/*************************************************************************** + testqgspointcloudrendererregistry.cpp + ----------------------- + begin : November 2020 + copyright : (C) 2020 by Nyall Dawson + email : nyall dot dawson at gmail dot com + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgspointcloudrendererregistry.h" +#include "qgspointcloudrenderer.h" +#include "qgsrendercontext.h" +#include "qgsreadwritecontext.h" + +#include +#include "qgstest.h" + +//dummy renderer for testing +class DummyRenderer : public QgsPointCloudRenderer +{ + public: + DummyRenderer() = default; + QgsPointCloudRenderer *clone() const override { return new DummyRenderer(); } + static QgsPointCloudRenderer *create( QDomElement &, const QgsReadWriteContext & ) { return new DummyRenderer(); } + void renderBlock( const QgsPointCloudBlock *, QgsPointCloudRenderContext & ) override {} + QDomElement save( QDomDocument &doc, const QgsReadWriteContext & ) const override { return doc.createElement( QStringLiteral( "test" ) ); } + +}; + +class TestQgsPointCloudRendererRegistry : public QObject +{ + Q_OBJECT + + private slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + void metadata(); + void createInstance(); + void instanceHasDefaultRenderers(); + void addRenderer(); + void fetchTypes(); + + private: + +}; + +void TestQgsPointCloudRendererRegistry::initTestCase() +{ + QgsApplication::init(); // init paths for CRS lookup + QgsApplication::initQgis(); +} + +void TestQgsPointCloudRendererRegistry::cleanupTestCase() +{ + QgsApplication::exitQgis(); +} + +void TestQgsPointCloudRendererRegistry::init() +{ + +} + +void TestQgsPointCloudRendererRegistry::cleanup() +{ + +} + +void TestQgsPointCloudRendererRegistry::metadata() +{ + QgsPointCloudRendererMetadata metadata = QgsPointCloudRendererMetadata( QStringLiteral( "name" ), QStringLiteral( "display name" ), DummyRenderer::create, QIcon() ); + QCOMPARE( metadata.name(), QString( "name" ) ); + QCOMPARE( metadata.visibleName(), QString( "display name" ) ); + + //test creating renderer from metadata + QVariantMap map; + QDomElement elem; + std::unique_ptr< QgsPointCloudRenderer > renderer( metadata.createRenderer( elem, QgsReadWriteContext() ) ); + QVERIFY( renderer ); + DummyRenderer *dummyRenderer = dynamic_cast( renderer.get() ); + QVERIFY( dummyRenderer ); +} + +void TestQgsPointCloudRendererRegistry::createInstance() +{ + QgsPointCloudRendererRegistry *registry = QgsApplication::pointCloudRendererRegistry(); + QVERIFY( registry ); +} + +void TestQgsPointCloudRendererRegistry::instanceHasDefaultRenderers() +{ + //check that callout registry is initially populated with some renderers + //(assumes that there is some default callouts) + QgsPointCloudRendererRegistry *registry = QgsApplication::pointCloudRendererRegistry(); + QVERIFY( registry->renderersList().length() > 0 ); +} + +void TestQgsPointCloudRendererRegistry::addRenderer() +{ + QgsPointCloudRendererRegistry *registry = QgsApplication::pointCloudRendererRegistry(); + int previousCount = registry->renderersList().length(); + + registry->addRenderer( new QgsPointCloudRendererMetadata( QStringLiteral( "Dummy" ), QStringLiteral( "Dummy renderer" ), DummyRenderer::create, QIcon() ) ); + QCOMPARE( registry->renderersList().length(), previousCount + 1 ); + //try adding again, should have no effect + QgsPointCloudRendererMetadata *dupe = new QgsPointCloudRendererMetadata( QStringLiteral( "Dummy" ), QStringLiteral( "Dummy callout" ), DummyRenderer::create, QIcon() ); + QVERIFY( ! registry->addRenderer( dupe ) ); + QCOMPARE( registry->renderersList().length(), previousCount + 1 ); + delete dupe; + + //try adding empty metadata + registry->addRenderer( nullptr ); + QCOMPARE( registry->renderersList().length(), previousCount + 1 ); +} + +void TestQgsPointCloudRendererRegistry::fetchTypes() +{ + QgsPointCloudRendererRegistry *registry = QgsApplication::pointCloudRendererRegistry(); + QStringList types = registry->renderersList(); + + QVERIFY( types.contains( "Dummy" ) ); + + QgsPointCloudRendererAbstractMetadata *metadata = registry->rendererMetadata( QStringLiteral( "Dummy" ) ); + QCOMPARE( metadata->name(), QString( "Dummy" ) ); + + //metadata for bad renderer + metadata = registry->rendererMetadata( QStringLiteral( "bad renderer" ) ); + QVERIFY( !metadata ); +} + +QGSTEST_MAIN( TestQgsPointCloudRendererRegistry ) +#include "testqgspointcloudrendererregistry.moc"