Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Implement legends for point clouds with the Attribute By Ramp renderer
  • Loading branch information
nyalldawson committed Dec 2, 2020
1 parent d6ac6c4 commit 6a6b77a
Show file tree
Hide file tree
Showing 11 changed files with 152 additions and 4 deletions.
Expand Up @@ -41,6 +41,8 @@ Constructor for QgsPointCloudAttributeByRampRenderer.

virtual QSet< QString > usedAttributes( const QgsPointCloudRenderContext &context ) const;

virtual QList<QgsLayerTreeModelLegendNode *> createLegendNodes( QgsLayerTreeLayer *nodeLayer ) /Factory/;


static QgsPointCloudRenderer *create( QDomElement &element, const QgsReadWriteContext &context ) /Factory/;
%Docstring
Expand Down
Expand Up @@ -11,7 +11,6 @@




class QgsPointCloudRenderContext
{
%Docstring
Expand Down Expand Up @@ -331,6 +330,11 @@ Sets the ``unit`` for the maximum screen error allowed when rendering the point
.. seealso:: :py:func:`setMaximumScreenError`

.. seealso:: :py:func:`maximumScreenErrorUnit`
%End

virtual QList<QgsLayerTreeModelLegendNode *> createLegendNodes( QgsLayerTreeLayer *nodeLayer ) /Factory/;
%Docstring
Creates a set of legend nodes representing the renderer.
%End

protected:
Expand Down
29 changes: 29 additions & 0 deletions python/core/auto_generated/qgsmaplayerlegend.sip.in
Expand Up @@ -66,6 +66,13 @@ Create new legend implementation for raster layer
static QgsMapLayerLegend *defaultMeshLegend( QgsMeshLayer *ml ) /Factory/;
%Docstring
Create new legend implementation for mesh layer
%End

static QgsMapLayerLegend *defaultPointCloudLegend( QgsPointCloudLayer *layer ) /Factory/;
%Docstring
Create new legend implementation for a point cloud ``layer``.

.. versionadded:: 3.18
%End

signals:
Expand Down Expand Up @@ -303,6 +310,28 @@ Creates an instance for the given mesh layer
virtual QList<QgsLayerTreeModelLegendNode *> createLayerTreeModelLegendNodes( QgsLayerTreeLayer *nodeLayer ) /Factory/;


};

class QgsDefaultPointCloudLayerLegend : QgsMapLayerLegend
{
%Docstring
Default legend implementation for point cloud layers

.. versionadded:: 3.18
%End

%TypeHeaderCode
#include "qgsmaplayerlegend.h"
%End
public:
explicit QgsDefaultPointCloudLayerLegend( QgsPointCloudLayer *layer );
%Docstring
Creates an instance for the given point cloud layer
%End

virtual QList<QgsLayerTreeModelLegendNode *> createLayerTreeModelLegendNodes( QgsLayerTreeLayer *nodeLayer ) /Factory/;


};


Expand Down
15 changes: 15 additions & 0 deletions src/core/pointcloud/qgspointcloudattributebyramprenderer.cpp
Expand Up @@ -20,6 +20,7 @@
#include "qgsstyle.h"
#include "qgscolorramp.h"
#include "qgssymbollayerutils.h"
#include "qgslayertreemodellegendnode.h"

QgsPointCloudAttributeByRampRenderer::QgsPointCloudAttributeByRampRenderer()
{
Expand Down Expand Up @@ -194,6 +195,20 @@ QSet<QString> QgsPointCloudAttributeByRampRenderer::usedAttributes( const QgsPoi
return res;
}

QList<QgsLayerTreeModelLegendNode *> QgsPointCloudAttributeByRampRenderer::createLegendNodes( QgsLayerTreeLayer *nodeLayer )
{
QList<QgsLayerTreeModelLegendNode *> nodes;

QList< QPair< QString, QColor > > items;
mColorRampShader.legendSymbologyItems( items );
for ( const QPair< QString, QColor > &item : qgis::as_const( items ) )
{
nodes << new QgsRasterSymbolLegendNode( nodeLayer, item.second, item.first );
}

return nodes;
}

QString QgsPointCloudAttributeByRampRenderer::attribute() const
{
return mAttribute;
Expand Down
1 change: 1 addition & 0 deletions src/core/pointcloud/qgspointcloudattributebyramprenderer.h
Expand Up @@ -45,6 +45,7 @@ class CORE_EXPORT QgsPointCloudAttributeByRampRenderer : public QgsPointCloudRen
void startRender( QgsPointCloudRenderContext &context ) override;
void stopRender( QgsPointCloudRenderContext &context ) override;
QSet< QString > usedAttributes( const QgsPointCloudRenderContext &context ) const override;
QList<QgsLayerTreeModelLegendNode *> createLegendNodes( QgsLayerTreeLayer *nodeLayer ) override SIP_FACTORY;

/**
* Creates an RGB renderer from an XML \a element.
Expand Down
3 changes: 3 additions & 0 deletions src/core/pointcloud/qgspointcloudlayer.cpp
Expand Up @@ -29,6 +29,7 @@
#include "qgspainting.h"
#include "qgspointcloudrendererregistry.h"
#include "qgspointcloudlayerelevationproperties.h"
#include "qgsmaplayerlegend.h"

QgsPointCloudLayer::QgsPointCloudLayer( const QString &path,
const QString &baseName,
Expand All @@ -42,6 +43,8 @@ QgsPointCloudLayer::QgsPointCloudLayer( const QString &path,
QgsDataProvider::ProviderOptions providerOptions { options.transformContext };
setDataSource( path, baseName, providerLib, providerOptions, options.loadDefaultStyle );
}

setLegend( QgsMapLayerLegend::defaultPointCloudLegend( this ) );
}

QgsPointCloudLayer::~QgsPointCloudLayer() = default;
Expand Down
5 changes: 5 additions & 0 deletions src/core/pointcloud/qgspointcloudrenderer.cpp
Expand Up @@ -111,6 +111,11 @@ void QgsPointCloudRenderer::setMaximumScreenErrorUnit( QgsUnitTypes::RenderUnit
mMaximumScreenErrorUnit = unit;
}

QList<QgsLayerTreeModelLegendNode *> QgsPointCloudRenderer::createLegendNodes( QgsLayerTreeLayer * )
{
return QList<QgsLayerTreeModelLegendNode *>();
}

void QgsPointCloudRenderer::copyCommonProperties( QgsPointCloudRenderer *destination ) const
{
destination->setPointSize( mPointSize );
Expand Down
8 changes: 7 additions & 1 deletion src/core/pointcloud/qgspointcloudrenderer.h
Expand Up @@ -26,7 +26,8 @@
#include "qgspointcloudattribute.h"

class QgsPointCloudBlock;

class QgsLayerTreeLayer;
class QgsLayerTreeModelLegendNode;

/**
* \ingroup core
Expand Down Expand Up @@ -381,6 +382,11 @@ class CORE_EXPORT QgsPointCloudRenderer
*/
void setMaximumScreenErrorUnit( QgsUnitTypes::RenderUnit unit );

/**
* Creates a set of legend nodes representing the renderer.
*/
virtual QList<QgsLayerTreeModelLegendNode *> createLegendNodes( QgsLayerTreeLayer *nodeLayer ) SIP_FACTORY;

protected:

/**
Expand Down
26 changes: 26 additions & 0 deletions src/core/qgsmaplayerlegend.cpp
Expand Up @@ -23,8 +23,10 @@
#include "qgsrasterlayer.h"
#include "qgsrenderer.h"
#include "qgsvectorlayer.h"
#include "qgspointcloudlayer.h"
#include "qgsdiagramrenderer.h"
#include "qgssymbollayerutils.h"
#include "qgspointcloudrenderer.h"

QgsMapLayerLegend::QgsMapLayerLegend( QObject *parent )
: QObject( parent )
Expand Down Expand Up @@ -59,6 +61,11 @@ QgsMapLayerLegend *QgsMapLayerLegend::defaultMeshLegend( QgsMeshLayer *ml )
return new QgsDefaultMeshLayerLegend( ml );
}

QgsMapLayerLegend *QgsMapLayerLegend::defaultPointCloudLegend( QgsPointCloudLayer *layer )
{
return new QgsDefaultPointCloudLayerLegend( layer );
}

// -------------------------------------------------------------------------


Expand Down Expand Up @@ -493,3 +500,22 @@ QList<QgsLayerTreeModelLegendNode *> QgsDefaultMeshLayerLegend::createLayerTreeM

return nodes;
}

//
// QgsDefaultPointCloudLayerLegend
//

QgsDefaultPointCloudLayerLegend::QgsDefaultPointCloudLayerLegend( QgsPointCloudLayer *layer )
: mLayer( layer )
{
connect( mLayer, &QgsMapLayer::rendererChanged, this, &QgsMapLayerLegend::itemsChanged );
}

QList<QgsLayerTreeModelLegendNode *> QgsDefaultPointCloudLayerLegend::createLayerTreeModelLegendNodes( QgsLayerTreeLayer *nodeLayer )
{
QgsPointCloudRenderer *renderer = mLayer->renderer();
if ( !renderer )
return QList<QgsLayerTreeModelLegendNode *>();

return renderer->createLegendNodes( nodeLayer );
}
26 changes: 26 additions & 0 deletions src/core/qgsmaplayerlegend.h
Expand Up @@ -27,6 +27,7 @@ class QgsLayerTreeModelLegendNode;
class QgsMeshLayer;
class QgsPluginLayer;
class QgsRasterLayer;
class QgsPointCloudLayer;
class QgsReadWriteContext;
class QgsVectorLayer;
class QgsLegendPatchShape;
Expand Down Expand Up @@ -81,6 +82,12 @@ class CORE_EXPORT QgsMapLayerLegend : public QObject
//! Create new legend implementation for mesh layer
static QgsMapLayerLegend *defaultMeshLegend( QgsMeshLayer *ml ) SIP_FACTORY;

/**
* Create new legend implementation for a point cloud \a layer.
* \since QGIS 3.18
*/
static QgsMapLayerLegend *defaultPointCloudLegend( QgsPointCloudLayer *layer ) SIP_FACTORY;

signals:
//! Emitted when existing items/nodes got invalid and should be replaced by new ones
void itemsChanged();
Expand Down Expand Up @@ -297,5 +304,24 @@ class CORE_EXPORT QgsDefaultMeshLayerLegend : public QgsMapLayerLegend
QgsMeshLayer *mLayer = nullptr;
};

/**
* \ingroup core
* Default legend implementation for point cloud layers
* \since QGIS 3.18
*/
class CORE_EXPORT QgsDefaultPointCloudLayerLegend : public QgsMapLayerLegend
{
Q_OBJECT

public:
//! Creates an instance for the given point cloud layer
explicit QgsDefaultPointCloudLayerLegend( QgsPointCloudLayer *layer );

QList<QgsLayerTreeModelLegendNode *> createLayerTreeModelLegendNodes( QgsLayerTreeLayer *nodeLayer ) SIP_FACTORY override;

private:
QgsPointCloudLayer *mLayer = nullptr;
};


#endif // QGSMAPLAYERLEGEND_H
35 changes: 33 additions & 2 deletions tests/src/python/test_qgspointcloudattributebyramprenderer.py
Expand Up @@ -28,10 +28,11 @@
QgsCoordinateReferenceSystem,
QgsDoubleRange,
QgsColorRampShader,
QgsStyle
QgsStyle,
QgsLayerTreeLayer
)

from qgis.PyQt.QtCore import QDir, QSize
from qgis.PyQt.QtCore import QDir, QSize, Qt
from qgis.PyQt.QtGui import QPainter
from qgis.PyQt.QtXml import QDomDocument

Expand Down Expand Up @@ -132,6 +133,36 @@ def testUsedAttributes(self):

self.assertEqual(renderer.usedAttributes(prc), {'attr'})

def testLegend(self):
renderer = QgsPointCloudAttributeByRampRenderer()
renderer.setAttribute('Intensity')
renderer.setMinimum(200)
renderer.setMaximum(1000)
ramp = QgsStyle.defaultStyle().colorRamp("Viridis")
shader = QgsColorRampShader(200, 1000, ramp.clone())
shader.setClassificationMode(QgsColorRampShader.EqualInterval)
shader.classifyColorRamp(classes=5)
renderer.setColorRampShader(shader)

layer = QgsPointCloudLayer(unitTestDataPath() + '/point_clouds/ept/sunshine-coast/ept.json', 'test', 'ept')
layer_tree_layer = QgsLayerTreeLayer(layer)
nodes = renderer.createLegendNodes(layer_tree_layer)
self.assertEqual(len(nodes), 5)
self.assertEqual(nodes[0].data(Qt.DisplayRole), '200')
self.assertEqual(nodes[1].data(Qt.DisplayRole), '400')
self.assertEqual(nodes[2].data(Qt.DisplayRole), '600')
self.assertEqual(nodes[3].data(Qt.DisplayRole), '800')
self.assertEqual(nodes[4].data(Qt.DisplayRole), '1e+03')

shader = QgsColorRampShader(200, 600, ramp.clone())
shader.setClassificationMode(QgsColorRampShader.EqualInterval)
shader.classifyColorRamp(classes=2)
renderer.setColorRampShader(shader)
nodes = renderer.createLegendNodes(layer_tree_layer)
self.assertEqual(len(nodes), 2)
self.assertEqual(nodes[0].data(Qt.DisplayRole), '200')
self.assertEqual(nodes[1].data(Qt.DisplayRole), '600')

@unittest.skipIf('ept' not in QgsProviderRegistry.instance().providerList(), 'EPT provider not available')
def testRender(self):
layer = QgsPointCloudLayer(unitTestDataPath() + '/point_clouds/ept/sunshine-coast/ept.json', 'test', 'ept')
Expand Down

0 comments on commit 6a6b77a

Please sign in to comment.