Also add scaling factor option, test

This commit is contained in:
Nyall Dawson 2020-12-16 10:08:16 +10:00
parent e4c8ce8123
commit 7b141e92f0
22 changed files with 267 additions and 39 deletions

View File

@ -46,6 +46,10 @@ the layer.
This can be used to correct or manually adjust for incorrect elevation values in a point cloud layer.
.. note::
Any scaling specified via :py:func:`~QgsPointCloudLayerElevationProperties.zScale` is applied before any offset value specified via :py:func:`~QgsPointCloudLayerElevationProperties.zOffset`
.. seealso:: :py:func:`setZOffset`
%End
@ -56,7 +60,41 @@ the layer.
This can be used to correct or manually adjust for incorrect elevation values in a point cloud layer.
.. note::
Any scaling specified via :py:func:`~QgsPointCloudLayerElevationProperties.zScale` is applied before any offset value specified via :py:func:`~QgsPointCloudLayerElevationProperties.zOffset`
.. seealso:: :py:func:`zOffset`
%End
double zScale() const;
%Docstring
Returns the z scale, which is a scaling factor which should be applied to z values from
the layer.
This can be used to correct or manually adjust for incorrect elevation values in a point cloud layer, such
as conversion of elevation values in feet to meters.
.. note::
Any scaling specified via :py:func:`~QgsPointCloudLayerElevationProperties.zScale` is applied before any offset value specified via :py:func:`~QgsPointCloudLayerElevationProperties.zOffset`
.. seealso:: :py:func:`setZScale`
%End
void setZScale( double scale );
%Docstring
Sets the z ``scale``, which is a scaling factor which will be applied to z values from
the layer.
This can be used to correct or manually adjust for incorrect elevation values in a point cloud layer, such
as conversion of elevation values in feet to meters.
.. note::
Any scaling specified via :py:func:`~QgsPointCloudLayerElevationProperties.zScale` is applied before any offset value specified via :py:func:`~QgsPointCloudLayerElevationProperties.zOffset`
.. seealso:: :py:func:`zScale`
%End
};

View File

@ -26,13 +26,16 @@ Encapsulates the render context for a 2D point cloud rendering operation.
public:
QgsPointCloudRenderContext( QgsRenderContext &context, const QgsVector3D &scale, const QgsVector3D &offset,
double zValueFixedOffset );
double zValueScale, double zValueFixedOffset );
%Docstring
Constructor for QgsPointCloudRenderContext.
The ``scale`` and ``offset`` arguments specify the scale and offset of the layer's int32 coordinates
compared to CRS coordinates respectively.
The ``zValueScale`` argument specifies any constant scaling factor which must be applied to z values
taken from the point cloud index.
The ``zValueFixedOffset`` argument specifies any constant offset value which must be added to z values
taken from the point cloud index.
%End
@ -114,9 +117,22 @@ Returns the offset for the y value in a point record.
.. seealso:: :py:func:`yOffset`
%End
double zValueScale() const;
%Docstring
Returns any constant scaling factor which must be applied to z values taken from the point cloud index.
.. note::
Scaling of z values should be applied before the :py:func:`~QgsPointCloudRenderContext.zValueFixedOffset`.
%End
double zValueFixedOffset() const;
%Docstring
Returns any constant offset which must be applied to z values taken from the point cloud index.
.. note::
Scaling of z values via :py:func:`~QgsPointCloudRenderContext.zValueScale` should be applied before the :py:func:`~QgsPointCloudRenderContext.zValueFixedOffset`.
%End

View File

@ -29,9 +29,10 @@
#include "qgis.h"
QgsPointCloud3DRenderContext::QgsPointCloud3DRenderContext( const Qgs3DMapSettings &map, std::unique_ptr<QgsPointCloud3DSymbol> symbol, double zValueFixedOffset )
QgsPointCloud3DRenderContext::QgsPointCloud3DRenderContext( const Qgs3DMapSettings &map, std::unique_ptr<QgsPointCloud3DSymbol> symbol, double zValueScale, double zValueFixedOffset )
: Qgs3DRenderContext( map )
, mSymbol( std::move( symbol ) )
, mZValueScale( zValueScale )
, mZValueFixedOffset( zValueFixedOffset )
{
@ -115,6 +116,7 @@ Qt3DCore::QEntity *QgsPointCloudLayer3DRenderer::createEntity( const Qgs3DMapSet
return nullptr;
return new QgsPointCloudLayerChunkedEntity( pcl->dataProvider()->index(), map, dynamic_cast<QgsPointCloud3DSymbol *>( mSymbol->clone() ), maximumScreenError(), showBoundingBoxes(),
static_cast< const QgsPointCloudLayerElevationProperties * >( pcl->elevationProperties() )->zScale(),
static_cast< const QgsPointCloudLayerElevationProperties * >( pcl->elevationProperties() )->zOffset() );
}

View File

@ -46,11 +46,14 @@ class _3D_NO_EXPORT QgsPointCloud3DRenderContext : public Qgs3DRenderContext
/**
* Constructor for QgsPointCloud3DRenderContext.
*
* The \a zValueScale argument specifies any constant scaling factor which must be applied to z values
* taken from the point cloud index.
*
* The \a zValueFixedOffset argument specifies any constant offset value which must be added to z values
* taken from the point cloud index.
*/
QgsPointCloud3DRenderContext( const Qgs3DMapSettings &map, std::unique_ptr< QgsPointCloud3DSymbol > symbol,
double zValueFixedOffset );
double zValueScale, double zValueFixedOffset );
//! QgsPointCloudRenderContext cannot be copied.
QgsPointCloud3DRenderContext( const QgsPointCloud3DRenderContext &rh ) = delete;
@ -133,8 +136,17 @@ class _3D_NO_EXPORT QgsPointCloud3DRenderContext : public Qgs3DRenderContext
}
}
/**
* Returns any constant scaling factor which must be applied to z values taken from the point cloud index.
*
* \note Scaling of z values should be applied before the zValueFixedOffset().
*/
double zValueScale() const { return mZValueScale; }
/**
* Returns any constant offset which must be applied to z values taken from the point cloud index.
*
* \note Scaling of z values via zValueScale() should be applied before the zValueFixedOffset().
*/
double zValueFixedOffset() const { return mZValueFixedOffset; }
@ -145,6 +157,7 @@ class _3D_NO_EXPORT QgsPointCloud3DRenderContext : public Qgs3DRenderContext
QgsPointCloudAttributeCollection mAttributes;
std::unique_ptr<QgsPointCloud3DSymbol> mSymbol;
QgsPointCloudCategoryList mFilteredOutCategories;
double mZValueScale = 1.0;
double mZValueFixedOffset = 0;
};

View File

@ -46,10 +46,11 @@
///////////////
QgsPointCloudLayerChunkLoader::QgsPointCloudLayerChunkLoader( const QgsPointCloudLayerChunkLoaderFactory *factory, QgsChunkNode *node, std::unique_ptr< QgsPointCloud3DSymbol > symbol, double zValueOffset )
QgsPointCloudLayerChunkLoader::QgsPointCloudLayerChunkLoader( const QgsPointCloudLayerChunkLoaderFactory *factory, QgsChunkNode *node, std::unique_ptr< QgsPointCloud3DSymbol > symbol,
double zValueScale, double zValueOffset )
: QgsChunkLoader( node )
, mFactory( factory )
, mContext( factory->mMap, std::move( symbol ), zValueOffset )
, mContext( factory->mMap, std::move( symbol ), zValueScale, zValueOffset )
{
QgsPointCloudIndex *pc = mFactory->mPointCloudIndex;
mContext.setAttributes( pc->attributes() );
@ -126,9 +127,11 @@ Qt3DCore::QEntity *QgsPointCloudLayerChunkLoader::createEntity( Qt3DCore::QEntit
///////////////
QgsPointCloudLayerChunkLoaderFactory::QgsPointCloudLayerChunkLoaderFactory( const Qgs3DMapSettings &map, QgsPointCloudIndex *pc, QgsPointCloud3DSymbol *symbol, double zValueOffset )
QgsPointCloudLayerChunkLoaderFactory::QgsPointCloudLayerChunkLoaderFactory( const Qgs3DMapSettings &map, QgsPointCloudIndex *pc, QgsPointCloud3DSymbol *symbol,
double zValueScale, double zValueOffset )
: mMap( map )
, mPointCloudIndex( pc )
, mZValueScale( zValueScale )
, mZValueOffset( zValueOffset )
{
mSymbol.reset( symbol );
@ -138,7 +141,7 @@ QgsChunkLoader *QgsPointCloudLayerChunkLoaderFactory::createChunkLoader( QgsChun
{
QgsChunkNodeId id = node->tileId();
Q_ASSERT( mPointCloudIndex->hasNode( IndexedPointCloudNode( id.d, id.x, id.y, id.z ) ) );
return new QgsPointCloudLayerChunkLoader( this, node, std::unique_ptr< QgsPointCloud3DSymbol >( static_cast< QgsPointCloud3DSymbol * >( mSymbol->clone() ) ), mZValueOffset );
return new QgsPointCloudLayerChunkLoader( this, node, std::unique_ptr< QgsPointCloud3DSymbol >( static_cast< QgsPointCloud3DSymbol * >( mSymbol->clone() ) ), mZValueScale, mZValueOffset );
}
QgsAABB nodeBoundsToAABB( QgsPointCloudDataBounds nodeBounds, QgsVector3D offset, QgsVector3D scale, const Qgs3DMapSettings &map, double zValueOffset );
@ -197,9 +200,9 @@ QgsAABB nodeBoundsToAABB( QgsPointCloudDataBounds nodeBounds, QgsVector3D offset
}
QgsPointCloudLayerChunkedEntity::QgsPointCloudLayerChunkedEntity( QgsPointCloudIndex *pc, const Qgs3DMapSettings &map, QgsPointCloud3DSymbol *symbol, float maxScreenError, bool showBoundingBoxes, double zValueOffset )
QgsPointCloudLayerChunkedEntity::QgsPointCloudLayerChunkedEntity( QgsPointCloudIndex *pc, const Qgs3DMapSettings &map, QgsPointCloud3DSymbol *symbol, float maxScreenError, bool showBoundingBoxes, double zValueScale, double zValueOffset )
: QgsChunkedEntity( maxScreenError,
new QgsPointCloudLayerChunkLoaderFactory( map, pc, symbol, zValueOffset ), true )
new QgsPointCloudLayerChunkLoaderFactory( map, pc, symbol, zValueScale, zValueOffset ), true )
{
setUsingAdditiveStrategy( true );
setShowBoundingBoxes( showBoundingBoxes );

View File

@ -54,11 +54,13 @@
class QgsPointCloudLayerChunkLoaderFactory : public QgsChunkLoaderFactory
{
public:
/*
/**
* Constructs the factory
* The factory takes ownership over the passed \a symbol
*/
QgsPointCloudLayerChunkLoaderFactory( const Qgs3DMapSettings &map, QgsPointCloudIndex *pc, QgsPointCloud3DSymbol *symbol, double zValueOffset );
QgsPointCloudLayerChunkLoaderFactory( const Qgs3DMapSettings &map, QgsPointCloudIndex *pc, QgsPointCloud3DSymbol *symbol,
double zValueScale, double zValueOffset );
//! Creates loader for the given chunk node. Ownership of the returned is passed to the caller.
virtual QgsChunkLoader *createChunkLoader( QgsChunkNode *node ) const override;
@ -67,6 +69,7 @@ class QgsPointCloudLayerChunkLoaderFactory : public QgsChunkLoaderFactory
const Qgs3DMapSettings &mMap;
QgsPointCloudIndex *mPointCloudIndex;
std::unique_ptr< QgsPointCloud3DSymbol > mSymbol;
double mZValueScale = 1.0;
double mZValueOffset = 0;
};
@ -87,7 +90,7 @@ class QgsPointCloudLayerChunkLoader : public QgsChunkLoader
* Constructs the loader
* QgsPointCloudLayerChunkLoader takes ownership over symbol
*/
QgsPointCloudLayerChunkLoader( const QgsPointCloudLayerChunkLoaderFactory *factory, QgsChunkNode *node, std::unique_ptr< QgsPointCloud3DSymbol > symbol, double zValueOffset );
QgsPointCloudLayerChunkLoader( const QgsPointCloudLayerChunkLoaderFactory *factory, QgsChunkNode *node, std::unique_ptr< QgsPointCloud3DSymbol > symbol, double zValueScale, double zValueOffset );
~QgsPointCloudLayerChunkLoader() override;
virtual void cancel() override;
@ -117,7 +120,7 @@ class QgsPointCloudLayerChunkedEntity : public QgsChunkedEntity
Q_OBJECT
public:
explicit QgsPointCloudLayerChunkedEntity( QgsPointCloudIndex *pc, const Qgs3DMapSettings &map, QgsPointCloud3DSymbol *symbol, float maxScreenError, bool showBoundingBoxes,
double zValueOffset );
double zValueScale, double zValueOffset );
~QgsPointCloudLayerChunkedEntity();
};

View File

@ -260,6 +260,7 @@ void QgsSingleColorPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex *p
const QgsVector3D scale = pc->scale();
const QgsVector3D offset = pc->offset();
const double zValueScale = context.zValueScale();
const double zValueOffset = context.zValueFixedOffset();
for ( int i = 0; i < count; ++i )
@ -270,7 +271,7 @@ void QgsSingleColorPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex *p
double x = offset.x() + scale.x() * ix;
double y = offset.y() + scale.y() * iy;
double z = offset.z() + scale.z() * iz + zValueOffset;
double z = ( offset.z() + scale.z() * iz ) * zValueScale + zValueOffset;
QgsVector3D point( x, y, z );
QgsVector3D p = context.map().mapToWorldCoordinates( point );
outNormal.positions.push_back( QVector3D( p.x(), p.y(), p.z() ) );
@ -315,6 +316,7 @@ void QgsColorRampPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex *pc,
bool attrIsZ = false;
QgsPointCloudAttribute::DataType attributeType = QgsPointCloudAttribute::Float;
int attributeOffset = 0;
const double zValueScale = context.zValueScale();
const double zValueOffset = context.zValueFixedOffset();
QgsColorRampPointCloud3DSymbol *symbol = dynamic_cast<QgsColorRampPointCloud3DSymbol *>( context.symbol() );
if ( symbol )
@ -371,7 +373,7 @@ void QgsColorRampPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex *pc,
double x = offset.x() + scale.x() * ix;
double y = offset.y() + scale.y() * iy;
double z = offset.z() + scale.z() * iz + zValueOffset;
double z = ( offset.z() + scale.z() * iz ) * zValueScale + zValueOffset;
QgsVector3D point( x, y, z );
QgsVector3D p = context.map().mapToWorldCoordinates( point );
@ -453,6 +455,7 @@ void QgsRGBPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex *pc, const
const QgsVector3D scale = pc->scale();
const QgsVector3D offset = pc->offset();
const double zValueScale = context.zValueScale();
const double zValueOffset = context.zValueFixedOffset();
QgsContrastEnhancement *redContrastEnhancement = symbol->redContrastEnhancement();
@ -473,7 +476,7 @@ void QgsRGBPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex *pc, const
qint32 iz = *( qint32 * )( ptr + i * recordSize + 8 );
double x = offset.x() + scale.x() * ix;
double y = offset.y() + scale.y() * iy;
double z = offset.z() + scale.z() * iz + zValueOffset;
double z = ( offset.z() + scale.z() * iz ) * zValueScale + zValueOffset;
QgsVector3D point( x, y, z );
QgsVector3D p = context.map().mapToWorldCoordinates( point );
@ -598,6 +601,7 @@ void QgsClassificationPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex
const QgsVector3D scale = pc->scale();
const QgsVector3D offset = pc->offset();
const double zValueScale = context.zValueScale();
const double zValueOffset = context.zValueFixedOffset();
QSet<int> filteredOutValues = context.getFilteredOutValues();
@ -609,7 +613,7 @@ void QgsClassificationPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex
double x = offset.x() + scale.x() * ix;
double y = offset.y() + scale.y() * iy;
double z = offset.z() + scale.z() * iz + zValueOffset;
double z = ( offset.z() + scale.z() * iz ) * zValueScale + zValueOffset;
QgsVector3D point( x, y, z );
QgsVector3D p = context.map().mapToWorldCoordinates( point );

View File

@ -27,10 +27,12 @@ QgsPointCloudElevationPropertiesWidget::QgsPointCloudElevationPropertiesWidget(
setupUi( this );
mOffsetZSpinBox->setClearValue( 0 );
mScaleZSpinBox->setClearValue( 1 );
syncToLayer( layer );
connect( mOffsetZSpinBox, qgis::overload<double >::of( &QDoubleSpinBox::valueChanged ), this, &QgsPointCloudElevationPropertiesWidget::onChanged );
connect( mScaleZSpinBox, qgis::overload<double >::of( &QDoubleSpinBox::valueChanged ), this, &QgsPointCloudElevationPropertiesWidget::onChanged );
}
void QgsPointCloudElevationPropertiesWidget::syncToLayer( QgsMapLayer *layer )
@ -41,6 +43,7 @@ void QgsPointCloudElevationPropertiesWidget::syncToLayer( QgsMapLayer *layer )
mBlockUpdates = true;
mOffsetZSpinBox->setValue( static_cast< const QgsPointCloudLayerElevationProperties * >( mLayer->elevationProperties() )->zOffset() );
mScaleZSpinBox->setValue( static_cast< const QgsPointCloudLayerElevationProperties * >( mLayer->elevationProperties() )->zScale() );
mBlockUpdates = false;
}
@ -50,6 +53,7 @@ void QgsPointCloudElevationPropertiesWidget::apply()
return;
static_cast< QgsPointCloudLayerElevationProperties * >( mLayer->elevationProperties() )->setZOffset( mOffsetZSpinBox->value() );
static_cast< QgsPointCloudLayerElevationProperties * >( mLayer->elevationProperties() )->setZScale( mScaleZSpinBox->value() );
mLayer->trigger3DUpdate();
}

View File

@ -111,7 +111,7 @@ void QgsPointCloudAttributeByRampRenderer::renderBlock( const QgsPointCloudBlock
if ( applyYOffset )
attributeValue = context.offset().y() + context.scale().y() * attributeValue;
if ( applyZOffset )
attributeValue = context.offset().z() + context.scale().z() * attributeValue + context.zValueFixedOffset();
attributeValue = ( context.offset().z() + context.scale().z() * attributeValue ) * context.zValueScale() + context.zValueFixedOffset();
mColorRampShader.shade( attributeValue, &red, &green, &blue, &alpha );
drawPoint( x, y, QColor( red, green, blue, alpha ), context );

View File

@ -32,6 +32,7 @@ QDomElement QgsPointCloudLayerElevationProperties::writeXml( QDomElement &parent
{
QDomElement element = document.createElement( QStringLiteral( "elevation" ) );
element.setAttribute( QStringLiteral( "zoffset" ), qgsDoubleToString( mZOffset ) );
element.setAttribute( QStringLiteral( "zscale" ), qgsDoubleToString( mZScale ) );
parentElement.appendChild( element );
return element;
}
@ -40,6 +41,7 @@ bool QgsPointCloudLayerElevationProperties::readXml( const QDomElement &element,
{
QDomElement elevationElement = element.firstChildElement( QStringLiteral( "elevation" ) ).toElement();
mZOffset = elevationElement.attribute( QStringLiteral( "zoffset" ), QStringLiteral( "0" ) ).toDouble();
mZScale = elevationElement.attribute( QStringLiteral( "zscale" ), QStringLiteral( "1" ) ).toDouble();
return true;
}
@ -60,7 +62,7 @@ QgsDoubleRange QgsPointCloudLayerElevationProperties::calculateZRange( QgsMapLay
const QVariant zMax = pcLayer->dataProvider()->metadataStatistic( QStringLiteral( "Z" ), QgsStatisticalSummary::Max );
if ( zMin.isValid() && zMax.isValid() )
{
return QgsDoubleRange( zMin.toDouble() + mZOffset, zMax.toDouble() + mZOffset );
return QgsDoubleRange( zMin.toDouble() * mZScale + mZOffset, zMax.toDouble() * mZScale + mZOffset );
}
}
}

View File

@ -54,6 +54,8 @@ class CORE_EXPORT QgsPointCloudLayerElevationProperties : public QgsMapLayerElev
*
* This can be used to correct or manually adjust for incorrect elevation values in a point cloud layer.
*
* \note Any scaling specified via zScale() is applied before any offset value specified via zOffset()
*
* \see setZOffset()
*/
double zOffset() const { return mZOffset; }
@ -64,12 +66,41 @@ class CORE_EXPORT QgsPointCloudLayerElevationProperties : public QgsMapLayerElev
*
* This can be used to correct or manually adjust for incorrect elevation values in a point cloud layer.
*
* \note Any scaling specified via zScale() is applied before any offset value specified via zOffset()
*
* \see zOffset()
*/
void setZOffset( double offset ) { mZOffset = offset; }
/**
* Returns the z scale, which is a scaling factor which should be applied to z values from
* the layer.
*
* This can be used to correct or manually adjust for incorrect elevation values in a point cloud layer, such
* as conversion of elevation values in feet to meters.
*
* \note Any scaling specified via zScale() is applied before any offset value specified via zOffset()
*
* \see setZScale()
*/
double zScale() const { return mZScale; }
/**
* Sets the z \a scale, which is a scaling factor which will be applied to z values from
* the layer.
*
* This can be used to correct or manually adjust for incorrect elevation values in a point cloud layer, such
* as conversion of elevation values in feet to meters.
*
* \note Any scaling specified via zScale() is applied before any offset value specified via zOffset()
*
* \see zScale()
*/
void setZScale( double scale ) { mZScale = scale; }
private:
double mZScale = 1.0;
double mZOffset = 0.0;
};

View File

@ -34,6 +34,7 @@
QgsPointCloudLayerRenderer::QgsPointCloudLayerRenderer( QgsPointCloudLayer *layer, QgsRenderContext &context )
: QgsMapLayerRenderer( layer->id(), &context )
, mLayer( layer )
, mLayerAttributes( layer->attributes() )
{
// TODO: we must not keep pointer to mLayer (it's dangerous) - we must copy anything we need for rendering
// or use some locking to prevent read/write from multiple threads
@ -48,14 +49,18 @@ QgsPointCloudLayerRenderer::QgsPointCloudLayerRenderer( QgsPointCloudLayer *laye
mOffset = mLayer->dataProvider()->index()->offset();
}
mZOffset = static_cast< const QgsPointCloudLayerElevationProperties * >( mLayer->elevationProperties() )->zOffset();
if ( const QgsPointCloudLayerElevationProperties *elevationProps = dynamic_cast< const QgsPointCloudLayerElevationProperties * >( mLayer->elevationProperties() ) )
{
mZOffset = elevationProps->zOffset();
mZScale = elevationProps->zScale();
}
mCloudExtent = mLayer->dataProvider()->polygonBounds();
}
bool QgsPointCloudLayerRenderer::render()
{
QgsPointCloudRenderContext context( *renderContext(), mScale, mOffset, mZOffset );
QgsPointCloudRenderContext context( *renderContext(), mScale, mOffset, mZScale, mZOffset );
// Set up the render configuration options
QPainter *painter = context.renderContext().painter();
@ -93,14 +98,14 @@ bool QgsPointCloudLayerRenderer::render()
if ( mAttributes.indexOf( attribute ) >= 0 )
continue; // don't re-add attributes we are already going to fetch
const int layerIndex = mLayer->attributes().indexOf( attribute );
const int layerIndex = mLayerAttributes.indexOf( attribute );
if ( layerIndex < 0 )
{
QgsMessageLog::logMessage( QObject::tr( "Required attribute %1 not found in layer" ).arg( attribute ), QObject::tr( "Point Cloud" ) );
continue;
}
mAttributes.push_back( mLayer->attributes().at( layerIndex ) );
mAttributes.push_back( mLayerAttributes.at( layerIndex ) );
}
QgsPointCloudDataBounds db;

View File

@ -69,7 +69,9 @@ class CORE_EXPORT QgsPointCloudLayerRenderer: public QgsMapLayerRenderer
QgsVector3D mScale;
QgsVector3D mOffset;
double mZOffset = 0;
double mZScale = 1.0;
QgsPointCloudAttributeCollection mLayerAttributes;
QgsPointCloudAttributeCollection mAttributes;
QgsGeometry mCloudExtent;

View File

@ -20,10 +20,11 @@
#include "qgsapplication.h"
#include "qgssymbollayerutils.h"
QgsPointCloudRenderContext::QgsPointCloudRenderContext( QgsRenderContext &context, const QgsVector3D &scale, const QgsVector3D &offset, double zValueFixedOffset )
QgsPointCloudRenderContext::QgsPointCloudRenderContext( QgsRenderContext &context, const QgsVector3D &scale, const QgsVector3D &offset, double zValueScale, double zValueFixedOffset )
: mRenderContext( context )
, mScale( scale )
, mOffset( offset )
, mZValueScale( zValueScale )
, mZValueFixedOffset( zValueFixedOffset )
{

View File

@ -47,11 +47,14 @@ class CORE_EXPORT QgsPointCloudRenderContext
* The \a scale and \a offset arguments specify the scale and offset of the layer's int32 coordinates
* compared to CRS coordinates respectively.
*
* The \a zValueScale argument specifies any constant scaling factor which must be applied to z values
* taken from the point cloud index.
*
* The \a zValueFixedOffset argument specifies any constant offset value which must be added to z values
* taken from the point cloud index.
*/
QgsPointCloudRenderContext( QgsRenderContext &context, const QgsVector3D &scale, const QgsVector3D &offset,
double zValueFixedOffset );
double zValueScale, double zValueFixedOffset );
//! QgsPointCloudRenderContext cannot be copied.
QgsPointCloudRenderContext( const QgsPointCloudRenderContext &rh ) = delete;
@ -136,8 +139,17 @@ class CORE_EXPORT QgsPointCloudRenderContext
*/
int zOffset() const { return mZOffset; }
/**
* Returns any constant scaling factor which must be applied to z values taken from the point cloud index.
*
* \note Scaling of z values should be applied before the zValueFixedOffset().
*/
double zValueScale() const { return mZValueScale; }
/**
* Returns any constant offset which must be applied to z values taken from the point cloud index.
*
* \note Scaling of z values via zValueScale() should be applied before the zValueFixedOffset().
*/
double zValueFixedOffset() const { return mZValueFixedOffset; }
@ -193,6 +205,7 @@ class CORE_EXPORT QgsPointCloudRenderContext
int mXOffset = 0;
int mYOffset = 0;
int mZOffset = 0;
double mZValueScale = 1.0;
double mZValueFixedOffset = 0;
};
@ -463,7 +476,7 @@ class CORE_EXPORT QgsPointCloudRenderer
static double pointZ( QgsPointCloudRenderContext &context, const char *ptr, int i )
{
const qint32 iz = *reinterpret_cast<const qint32 * >( ptr + i * context.pointRecordSize() + context.zOffset() );
return context.offset().z() + context.scale().z() * iz + context.zValueFixedOffset();
return ( context.offset().z() + context.scale().z() * iz ) * context.zValueScale() + context.zValueFixedOffset();
}
/**

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
<height>270</height>
</rect>
</property>
<property name="windowTitle">
@ -41,21 +41,37 @@
<string notr="true">vectorgeneral</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="2" column="0">
<widget class="Line" name="line_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
<item row="1" column="1">
<widget class="QgsDoubleSpinBox" name="mScaleZSpinBox">
<property name="decimals">
<number>6</number>
</property>
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="maximum">
<double>99999999999.000000000000000</double>
</property>
<property name="value">
<double>1.000000000000000</double>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Offset elevation</string>
<string>Scale</string>
</property>
</widget>
</item>
<item row="1" column="1">
<item row="2" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Offset</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QgsDoubleSpinBox" name="mOffsetZSpinBox">
<property name="decimals">
<number>6</number>
@ -68,6 +84,23 @@
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="Line" name="line_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="label_3">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Elevation scaling and offset can be used to manually correct elevation values in the point cloud at render time.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;The scale is applied to the point cloud elevation values before adding the offset.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>

View File

@ -244,8 +244,15 @@ void TestQgsEptProvider::calculateZRange()
QVERIFY( layer->isValid() );
QgsDoubleRange range = layer->elevationProperties()->calculateZRange( layer.get() );
QGSCOMPARENEAR( range.lower(), 1, 74.34 );
QGSCOMPARENEAR( range.upper(), 2, 80.02 );
QGSCOMPARENEAR( range.lower(), 74.34, 0.01 );
QGSCOMPARENEAR( range.upper(), 80.02, 0.01 );
static_cast< QgsPointCloudLayerElevationProperties * >( layer->elevationProperties() )->setZScale( 2 );
static_cast< QgsPointCloudLayerElevationProperties * >( layer->elevationProperties() )->setZOffset( 0.5 );
range = layer->elevationProperties()->calculateZRange( layer.get() );
QGSCOMPARENEAR( range.lower(), 149.18, 0.01 );
QGSCOMPARENEAR( range.upper(), 160.54, 0.01 );
}

View File

@ -213,6 +213,7 @@ ADD_PYTHON_TEST(PyQgsPointCloudAttributeComboBox test_qgspointcloudattributecomb
ADD_PYTHON_TEST(PyQgsPointCloudAttributeModel test_qgspointcloudattributemodel.py)
ADD_PYTHON_TEST(PyQgsPointCloudClassifiedRenderer test_qgspointcloudclassifiedrenderer.py)
ADD_PYTHON_TEST(PyQgsPointCloudDataProvider test_qgspointcloudprovider.py)
ADD_PYTHON_TEST(PyQgsPointCloudElevationProperties test_qgspointcloudelevationproperties.py)
ADD_PYTHON_TEST(PyQgsPointCloudExtentRenderer test_qgspointcloudextentrenderer.py)
ADD_PYTHON_TEST(PyQgsPointCloudRgbRenderer test_qgspointcloudrgbrenderer.py)
ADD_PYTHON_TEST(PyQgsPointClusterRenderer test_qgspointclusterrenderer.py)

View File

@ -129,7 +129,7 @@ class TestQgsPointCloudAttributeByRampRenderer(unittest.TestCase):
renderer.setAttribute('attr')
rc = QgsRenderContext()
prc = QgsPointCloudRenderContext(rc, QgsVector3D(), QgsVector3D())
prc = QgsPointCloudRenderContext(rc, QgsVector3D(), QgsVector3D(), 1, 0)
self.assertEqual(renderer.usedAttributes(prc), {'attr'})

View File

@ -107,7 +107,7 @@ class TestQgsPointCloudClassifiedRenderer(unittest.TestCase):
renderer.setAttribute('attr')
rc = QgsRenderContext()
prc = QgsPointCloudRenderContext(rc, QgsVector3D(), QgsVector3D())
prc = QgsPointCloudRenderContext(rc, QgsVector3D(), QgsVector3D(), 1, 0)
self.assertEqual(renderer.usedAttributes(prc), {'attr'})

View File

@ -0,0 +1,50 @@
# -*- coding: utf-8 -*-
"""QGIS Unit tests for QgsPointCloudLayerElevationProperties
.. note:: 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.
"""
__author__ = 'Nyall Dawson'
__date__ = '09/11/2020'
__copyright__ = 'Copyright 2020, The QGIS Project'
import qgis # NOQA
from qgis.core import (
QgsPointCloudLayerElevationProperties,
QgsReadWriteContext,
)
from qgis.PyQt.QtXml import QDomDocument
from qgis.testing import start_app, unittest
start_app()
class TestQgsPointCloudElevationProperties(unittest.TestCase):
def testBasic(self):
props = QgsPointCloudLayerElevationProperties(None)
self.assertEqual(props.zScale(), 1)
self.assertEqual(props.zOffset(), 0)
props.setZOffset(0.5)
props.setZScale(2)
self.assertEqual(props.zScale(), 2)
self.assertEqual(props.zOffset(), 0.5)
doc = QDomDocument("testdoc")
elem = doc.createElement('test')
props.writeXml(elem, doc, QgsReadWriteContext())
props2 = QgsPointCloudLayerElevationProperties(None)
props2.readXml(elem, QgsReadWriteContext())
self.assertEqual(props2.zScale(), 2)
self.assertEqual(props2.zOffset(), 0.5)
if __name__ == '__main__':
unittest.main()

View File

@ -170,7 +170,7 @@ class TestQgsPointCloudRgbRenderer(unittest.TestCase):
renderer.setRedAttribute('r')
rc = QgsRenderContext()
prc = QgsPointCloudRenderContext(rc, QgsVector3D(), QgsVector3D())
prc = QgsPointCloudRenderContext(rc, QgsVector3D(), QgsVector3D(), 1, 0)
self.assertEqual(renderer.usedAttributes(prc), {'r', 'g', 'b'})