Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Add choice of point style for 2d point cloud renders
Options are Square or Circle shapes
  • Loading branch information
nyalldawson authored and PeterPetrik committed Dec 3, 2020
1 parent 19fdf11 commit c48f29a
Show file tree
Hide file tree
Showing 10 changed files with 145 additions and 32 deletions.
20 changes: 20 additions & 0 deletions python/core/auto_generated/pointcloud/qgspointcloudrenderer.sip.in
Expand Up @@ -143,6 +143,12 @@ Abstract base class for 2d point cloud renderers.
%End
public:

enum PointSymbol
{
Square,
Circle,
};

QgsPointCloudRenderer();
%Docstring
Constructor for QgsPointCloudRenderer.
Expand Down Expand Up @@ -302,6 +308,20 @@ Returns the map unit scale used for the point size.
.. seealso:: :py:func:`pointSizeUnit`

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

PointSymbol pointSymbol() const;
%Docstring
Returns the symbol used by the renderer for drawing points.

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

void setPointSymbol( PointSymbol symbol );
%Docstring
Sets the ``symbol`` used by the renderer for drawing points.

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

double maximumScreenError() const;
Expand Down
2 changes: 0 additions & 2 deletions src/core/pointcloud/qgspointcloudattributebyramprenderer.cpp
Expand Up @@ -47,8 +47,6 @@ QgsPointCloudRenderer *QgsPointCloudAttributeByRampRenderer::clone() const

void QgsPointCloudAttributeByRampRenderer::renderBlock( const QgsPointCloudBlock *block, QgsPointCloudRenderContext &context )
{
const QgsMapToPixel mapToPixel = context.renderContext().mapToPixel();

const QgsRectangle visibleExtent = context.renderContext().extent();

const char *ptr = block->data();
Expand Down
14 changes: 6 additions & 8 deletions src/core/pointcloud/qgspointcloudlayerrenderer.cpp
Expand Up @@ -53,6 +53,12 @@ bool QgsPointCloudLayerRenderer::render()

QgsPointCloudRenderContext context( *renderContext(), mScale, mOffset );

// Set up the render configuration options
QPainter *painter = context.renderContext().painter();

QgsScopedQPainterState painterState( painter );
context.renderContext().setPainterFlagsUsingContext( painter );

mRenderer->startRender( context );

mAttributes.push_back( QgsPointCloudAttribute( QStringLiteral( "X" ), QgsPointCloudAttribute::Int32 ) );
Expand All @@ -79,14 +85,6 @@ bool QgsPointCloudLayerRenderer::render()
mAttributes.push_back( mLayer->attributes().at( layerIndex ) );
}

// Set up the render configuration options
QPainter *painter = context.renderContext().painter();

QgsScopedQPainterState painterState( painter );
context.renderContext().setPainterFlagsUsingContext( painter );
// for point clouds we always disable antialiasing -- it's not critical here and we benefit from the performance boost disabling it gives
context.renderContext().painter()->setRenderHint( QPainter::Antialiasing, false );

QgsPointCloudDataBounds db;

#ifdef QGISDEBUG
Expand Down
23 changes: 23 additions & 0 deletions src/core/pointcloud/qgspointcloudrenderer.cpp
Expand Up @@ -84,6 +84,16 @@ void QgsPointCloudRenderer::startRender( QgsPointCloudRenderContext &context )
#endif

mPainterPenWidth = context.renderContext().convertToPainterUnits( pointSize(), pointSizeUnit(), pointSizeMapUnitScale() );

switch ( mPointSymbol )
{
case Square:
// for square point we always disable antialiasing -- it's not critical here and we benefit from the performance boost disabling it gives
context.renderContext().painter()->setRenderHint( QPainter::Antialiasing, false );

case Circle:
break;
}
}

void QgsPointCloudRenderer::stopRender( QgsPointCloudRenderContext & )
Expand Down Expand Up @@ -142,6 +152,7 @@ void QgsPointCloudRenderer::copyCommonProperties( QgsPointCloudRenderer *destina
destination->setPointSizeMapUnitScale( mPointSizeMapUnitScale );
destination->setMaximumScreenError( mMaximumScreenError );
destination->setMaximumScreenErrorUnit( mMaximumScreenErrorUnit );
destination->setPointSymbol( mPointSymbol );
}

void QgsPointCloudRenderer::restoreCommonProperties( const QDomElement &element, const QgsReadWriteContext & )
Expand All @@ -152,6 +163,7 @@ void QgsPointCloudRenderer::restoreCommonProperties( const QDomElement &element,

mMaximumScreenError = element.attribute( QStringLiteral( "maximumScreenError" ), QStringLiteral( "1" ) ).toDouble();
mMaximumScreenErrorUnit = QgsUnitTypes::decodeRenderUnit( element.attribute( QStringLiteral( "maximumScreenErrorUnit" ), QStringLiteral( "MM" ) ) );
mPointSymbol = static_cast< PointSymbol >( element.attribute( QStringLiteral( "pointSymbol" ), QStringLiteral( "0" ) ).toInt() );
}

void QgsPointCloudRenderer::saveCommonProperties( QDomElement &element, const QgsReadWriteContext & ) const
Expand All @@ -162,6 +174,17 @@ void QgsPointCloudRenderer::saveCommonProperties( QDomElement &element, const Qg

element.setAttribute( QStringLiteral( "maximumScreenError" ), qgsDoubleToString( mMaximumScreenError ) );
element.setAttribute( QStringLiteral( "maximumScreenErrorUnit" ), QgsUnitTypes::encodeUnit( mMaximumScreenErrorUnit ) );
element.setAttribute( QStringLiteral( "pointSymbol" ), QString::number( mPointSymbol ) );
}

QgsPointCloudRenderer::PointSymbol QgsPointCloudRenderer::pointSymbol() const
{
return mPointSymbol;
}

void QgsPointCloudRenderer::setPointSymbol( PointSymbol symbol )
{
mPointSymbol = symbol;
}


50 changes: 41 additions & 9 deletions src/core/pointcloud/qgspointcloudrenderer.h
Expand Up @@ -216,6 +216,15 @@ class CORE_EXPORT QgsPointCloudRenderer

public:

/**
* Rendering symbols for points.
*/
enum PointSymbol
{
Square, //!< Renders points as squares
Circle, //!< Renders points as circles
};

/**
* Constructor for QgsPointCloudRenderer.
*/
Expand Down Expand Up @@ -358,6 +367,20 @@ class CORE_EXPORT QgsPointCloudRenderer
*/
const QgsMapUnitScale &pointSizeMapUnitScale() const { return mPointSizeMapUnitScale; }

/**
* Returns the symbol used by the renderer for drawing points.
*
* \see setPointSymbol()
*/
PointSymbol pointSymbol() const;

/**
* Sets the \a symbol used by the renderer for drawing points.
*
* \see pointSymbol()
*/
void setPointSymbol( PointSymbol symbol );

/**
* Returns the maximum screen error allowed when rendering the point cloud.
*
Expand Down Expand Up @@ -436,15 +459,23 @@ class CORE_EXPORT QgsPointCloudRenderer
void drawPoint( double x, double y, const QColor &color, QgsPointCloudRenderContext &context ) const
{
context.renderContext().mapToPixel().transformInPlace( x, y );
#if 0
pen.setColor( QColor( red, green, blue ) );
context.renderContext().painter()->setPen( pen );
context.renderContext().painter()->drawPoint( QPointF( x, y ) );
#else
context.renderContext().painter()->fillRect( QRectF( x - mPainterPenWidth * 0.5,
y - mPainterPenWidth * 0.5,
mPainterPenWidth, mPainterPenWidth ), color );
#endif
QPainter *painter = context.renderContext().painter();
switch ( mPointSymbol )
{
case Square:
painter->fillRect( QRectF( x - mPainterPenWidth * 0.5,
y - mPainterPenWidth * 0.5,
mPainterPenWidth, mPainterPenWidth ), color );
break;

case Circle:
painter->setBrush( QBrush( color ) );
painter->setPen( Qt::NoPen );
painter->drawEllipse( QRectF( x - mPainterPenWidth * 0.5,
y - mPainterPenWidth * 0.5,
mPainterPenWidth, mPainterPenWidth ) );
break;
};
}

/**
Expand Down Expand Up @@ -485,6 +516,7 @@ class CORE_EXPORT QgsPointCloudRenderer
QgsUnitTypes::RenderUnit mPointSizeUnit = QgsUnitTypes::RenderMillimeters;
QgsMapUnitScale mPointSizeMapUnitScale;

PointSymbol mPointSymbol = Square;
int mPainterPenWidth = 1;
};

Expand Down
2 changes: 0 additions & 2 deletions src/core/pointcloud/qgspointcloudrgbrenderer.cpp
Expand Up @@ -56,8 +56,6 @@ QgsPointCloudRenderer *QgsPointCloudRgbRenderer::clone() const

void QgsPointCloudRgbRenderer::renderBlock( const QgsPointCloudBlock *block, QgsPointCloudRenderContext &context )
{
const QgsMapToPixel mapToPixel = context.renderContext().mapToPixel();

const QgsRectangle visibleExtent = context.renderContext().extent();

const char *ptr = block->data();
Expand Down
9 changes: 9 additions & 0 deletions src/gui/pointcloud/qgspointcloudrendererpropertieswidget.cpp
Expand Up @@ -83,6 +83,9 @@ QgsPointCloudRendererPropertiesWidget::QgsPointCloudRendererPropertiesWidget( Qg

cboRenderers->setCurrentIndex( -1 ); // set no current renderer

mPointStyleComboBox->addItem( tr( "Square" ), QgsPointCloudRenderer::Square );
mPointStyleComboBox->addItem( tr( "Circle" ), QgsPointCloudRenderer::Circle );

connect( cboRenderers, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsPointCloudRendererPropertiesWidget::rendererChanged );

connect( mBlendModeComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsPointCloudRendererPropertiesWidget::emitWidgetChanged );
Expand All @@ -100,6 +103,8 @@ QgsPointCloudRendererPropertiesWidget::QgsPointCloudRendererPropertiesWidget( Qg
connect( mMaxErrorSpinBox, qgis::overload<double>::of( &QgsDoubleSpinBox::valueChanged ), this, &QgsPointCloudRendererPropertiesWidget::emitWidgetChanged );
connect( mMaxErrorUnitWidget, &QgsUnitSelectionWidget::changed, this, &QgsPointCloudRendererPropertiesWidget::emitWidgetChanged );

connect( mPointStyleComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsPointCloudRendererPropertiesWidget::emitWidgetChanged );

syncToLayer( layer );
}

Expand Down Expand Up @@ -136,6 +141,8 @@ void QgsPointCloudRendererPropertiesWidget::syncToLayer( QgsMapLayer *layer )
mPointSizeUnitWidget->setUnit( mLayer->renderer()->pointSizeUnit() );
mPointSizeUnitWidget->setMapUnitScale( mLayer->renderer()->pointSizeMapUnitScale() );

mPointStyleComboBox->setCurrentIndex( mPointStyleComboBox->findData( mLayer->renderer()->pointSymbol() ) );

mMaxErrorSpinBox->setValue( mLayer->renderer()->maximumScreenError() );
mMaxErrorUnitWidget->setUnit( mLayer->renderer()->maximumScreenErrorUnit() );
}
Expand Down Expand Up @@ -167,6 +174,8 @@ void QgsPointCloudRendererPropertiesWidget::apply()
mLayer->renderer()->setPointSizeUnit( mPointSizeUnitWidget->unit() );
mLayer->renderer()->setPointSizeMapUnitScale( mPointSizeUnitWidget->getMapUnitScale() );

mLayer->renderer()->setPointSymbol( static_cast< QgsPointCloudRenderer::PointSymbol >( mPointStyleComboBox->currentData().toInt() ) );

mLayer->renderer()->setMaximumScreenError( mMaxErrorSpinBox->value() );
mLayer->renderer()->setMaximumScreenErrorUnit( mMaxErrorUnitWidget->unit() );
}
Expand Down
30 changes: 20 additions & 10 deletions src/ui/pointcloud/qgspointcloudrendererpropsdialogbase.ui
Expand Up @@ -108,16 +108,6 @@
<property name="rightMargin">
<number>3</number>
</property>
<item row="0" column="1" colspan="2">
<widget class="QgsDoubleSpinBox" name="mPointSizeSpinBox">
<property name="decimals">
<number>6</number>
</property>
<property name="maximum">
<double>99999999999.000000000000000</double>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QgsUnitSelectionWidget" name="mPointSizeUnitWidget">
<property name="minimumSize">
Expand All @@ -131,13 +121,33 @@
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QgsDoubleSpinBox" name="mPointSizeSpinBox">
<property name="decimals">
<number>6</number>
</property>
<property name="maximum">
<double>99999999999.000000000000000</double>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="lblTransparency_4">
<property name="text">
<string>Point size</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Style</string>
</property>
</widget>
</item>
<item row="1" column="1" colspan="3">
<widget class="QComboBox" name="mPointStyleComboBox"/>
</item>
</layout>
</widget>
</item>
Expand Down
27 changes: 26 additions & 1 deletion tests/src/python/test_qgspointcloudrgbrenderer.py
Expand Up @@ -27,7 +27,8 @@
QgsUnitTypes,
QgsMapUnitScale,
QgsCoordinateReferenceSystem,
QgsDoubleRange
QgsDoubleRange,
QgsPointCloudRenderer
)

from qgis.PyQt.QtCore import QDir, QSize
Expand Down Expand Up @@ -196,6 +197,30 @@ def testRender(self):
TestQgsPointCloudRgbRenderer.report += renderchecker.report()
self.assertTrue(result)

@unittest.skipIf('ept' not in QgsProviderRegistry.instance().providerList(), 'EPT provider not available')
def testRenderCircles(self):
layer = QgsPointCloudLayer(unitTestDataPath() + '/point_clouds/ept/rgb/ept.json', 'test', 'ept')
self.assertTrue(layer.isValid())

layer.renderer().setPointSize(3)
layer.renderer().setPointSizeUnit(QgsUnitTypes.RenderMillimeters)
layer.renderer().setPointSymbol(QgsPointCloudRenderer.Circle)

mapsettings = QgsMapSettings()
mapsettings.setOutputSize(QSize(400, 400))
mapsettings.setOutputDpi(96)
mapsettings.setDestinationCrs(layer.crs())
mapsettings.setExtent(QgsRectangle(497753.5, 7050887.5, 497754.6, 7050888.6))
mapsettings.setLayers([layer])

renderchecker = QgsMultiRenderChecker()
renderchecker.setMapSettings(mapsettings)
renderchecker.setControlPathPrefix('pointcloudrenderer')
renderchecker.setControlName('expected_rgb_circle_render')
result = renderchecker.runTest('expected_rgb_circle_render')
TestQgsPointCloudRgbRenderer.report += renderchecker.report()
self.assertTrue(result)

@unittest.skipIf('ept' not in QgsProviderRegistry.instance().providerList(), 'EPT provider not available')
def testRenderCrsTransform(self):
layer = QgsPointCloudLayer(unitTestDataPath() + '/point_clouds/ept/rgb/ept.json', 'test', 'ept')
Expand Down
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit c48f29a

Please sign in to comment.