mirror of
https://github.com/qgis/QGIS.git
synced 2025-02-24 00:47:57 -05:00
Also add scaling factor option, test
This commit is contained in:
parent
e4c8ce8123
commit
7b141e92f0
@ -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
|
||||
|
||||
};
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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() );
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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 );
|
||||
|
@ -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();
|
||||
};
|
||||
|
@ -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 );
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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 );
|
||||
|
@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 )
|
||||
{
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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><html><head/><body><p><span style=" font-weight:600;">Elevation scaling and offset can be used to manually correct elevation values in the point cloud at render time.</span></p><p>The scale is applied to the point cloud elevation values before adding the offset.</p></body></html></string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -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 );
|
||||
}
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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'})
|
||||
|
||||
|
@ -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'})
|
||||
|
||||
|
50
tests/src/python/test_qgspointcloudelevationproperties.py
Normal file
50
tests/src/python/test_qgspointcloudelevationproperties.py
Normal 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()
|
@ -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'})
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user