initial 3d classification rendering implementation

This commit is contained in:
NEDJIMAbelgacem 2020-12-05 18:44:29 +01:00 committed by Martin Dobias
parent b45eb5ca8d
commit 69f4476b59
12 changed files with 415 additions and 2 deletions

View File

@ -392,6 +392,56 @@ Ownership of ``enhancement`` is transferred.
QgsRgbPointCloud3DSymbol( const QgsRgbPointCloud3DSymbol &other );
};
class QgsClassificationPointCloud3DSymbol : QgsPointCloud3DSymbol
{
%TypeHeaderCode
#include "qgspointcloud3dsymbol.h"
%End
public:
QgsClassificationPointCloud3DSymbol();
%Docstring
Constructor for QgsClassificationPointCloud3DSymbol
%End
virtual QgsAbstract3DSymbol *clone() const /Factory/;
virtual QString symbolType() const;
virtual void writeXml( QDomElement &elem, const QgsReadWriteContext &context ) const;
virtual void readXml( const QDomElement &elem, const QgsReadWriteContext &context );
QString renderingParameter() const;
%Docstring
Returns the parameter used to select the color of the point cloud
.. seealso:: :py:func:`setRenderingParameter`
%End
void setRenderingParameter( const QString &parameter );
%Docstring
Sets the parameter used to select the color of the point cloud
.. seealso:: :py:func:`renderingParameter`
%End
QgsPointCloudCategoryList categoriesList() const;
void setCategoriesList( QgsPointCloudCategoryList categories );
QgsColorRampShader colorRampShader() const;
%Docstring
Returns the color ramp shader used to render the color
.. seealso:: :py:func:`setColorRampShader`
%End
virtual unsigned int byteStride();
};
/************************************************************************
* This file has been generated automatically from *
* *

View File

@ -140,6 +140,8 @@ void QgsPointCloudLayer3DRenderer::readXml( const QDomElement &elem, const QgsRe
mSymbol.reset( new QgsColorRampPointCloud3DSymbol );
else if ( symbolType == QLatin1String( "rgb" ) )
mSymbol.reset( new QgsRgbPointCloud3DSymbol );
else if ( symbolType == QLatin1String( "classification" ) )
mSymbol.reset( new QgsClassificationPointCloud3DSymbol );
else
mSymbol.reset();

View File

@ -67,6 +67,8 @@ QgsPointCloudLayerChunkLoader::QgsPointCloudLayerChunkLoader( const QgsPointClou
mHandler.reset( new QgsColorRampPointCloud3DSymbolHandler() );
else if ( symbol->symbolType() == QLatin1String( "rgb" ) )
mHandler.reset( new QgsRGBPointCloud3DSymbolHandler() );
else if ( symbol->symbolType() == QLatin1String( "classification" ) )
mHandler.reset( new QgsClassificationPointCloud3DSymbolHandler() );
//
// this will be run in a background thread

View File

@ -367,3 +367,134 @@ void QgsRgbPointCloud3DSymbol::setBlueContrastEnhancement( QgsContrastEnhancemen
mBlueContrastEnhancement.reset( enhancement );
}
// QgsClassificationPointCloud3DSymbol
QgsClassificationPointCloud3DSymbol::QgsClassificationPointCloud3DSymbol()
: QgsPointCloud3DSymbol()
{
}
QgsAbstract3DSymbol *QgsClassificationPointCloud3DSymbol::clone() const
{
QgsClassificationPointCloud3DSymbol *result = new QgsClassificationPointCloud3DSymbol;
result->mPointSize = mPointSize;
result->mRenderingParameter = mRenderingParameter;
result->mCategoriesList = mCategoriesList;
copyBaseSettings( result );
return result;
}
QString QgsClassificationPointCloud3DSymbol::symbolType() const
{
return QStringLiteral( "classification" );
}
void QgsClassificationPointCloud3DSymbol::writeXml( QDomElement &elem, const QgsReadWriteContext &context ) const
{
Q_UNUSED( context )
QDomDocument doc = elem.ownerDocument();
elem.setAttribute( QStringLiteral( "point-size" ), mPointSize );
elem.setAttribute( QStringLiteral( "rendering-parameter" ), mRenderingParameter );
// categories
QDomElement catsElem = doc.createElement( QStringLiteral( "categories" ) );
for ( const QgsPointCloudCategory &category : mCategoriesList )
{
QDomElement catElem = doc.createElement( QStringLiteral( "category" ) );
catElem.setAttribute( QStringLiteral( "value" ), QString::number( category.value() ) );
catElem.setAttribute( QStringLiteral( "label" ), category.label() );
catElem.setAttribute( QStringLiteral( "color" ), QgsSymbolLayerUtils::encodeColor( category.color() ) );
catElem.setAttribute( QStringLiteral( "render" ), category.renderState() ? "true" : "false" );
catsElem.appendChild( catElem );
}
elem.appendChild( catsElem );
}
void QgsClassificationPointCloud3DSymbol::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
{
Q_UNUSED( context )
mPointSize = elem.attribute( "point-size", QStringLiteral( "2.0" ) ).toFloat();
mRenderingParameter = elem.attribute( "rendering-parameter", QString() );
const QDomElement catsElem = elem.firstChildElement( QStringLiteral( "categories" ) );
if ( !catsElem.isNull() )
{
mCategoriesList.clear();
QDomElement catElem = catsElem.firstChildElement();
while ( !catElem.isNull() )
{
if ( catElem.tagName() == QLatin1String( "category" ) )
{
const int value = catElem.attribute( QStringLiteral( "value" ) ).toInt();
const QString label = catElem.attribute( QStringLiteral( "label" ) );
const bool render = catElem.attribute( QStringLiteral( "render" ) ) != QLatin1String( "false" );
const QColor color = QgsSymbolLayerUtils::decodeColor( catElem.attribute( QStringLiteral( "color" ) ) );
mCategoriesList.append( QgsPointCloudCategory( value, color, label, render ) );
}
catElem = catElem.nextSiblingElement();
}
}
}
QString QgsClassificationPointCloud3DSymbol::renderingParameter() const
{
return mRenderingParameter;
}
void QgsClassificationPointCloud3DSymbol::setRenderingParameter( const QString &parameter )
{
mRenderingParameter = parameter;
}
void QgsClassificationPointCloud3DSymbol::setCategoriesList( QgsPointCloudCategoryList categories )
{
mCategoriesList = categories;
}
QgsColorRampShader QgsClassificationPointCloud3DSymbol::colorRampShader() const
{
QgsColorRampShader colorRampShader;
colorRampShader.setColorRampType( QgsColorRampShader::Type::Exact );
colorRampShader.setClassificationMode( QgsColorRampShader::ClassificationMode::Continuous );
QList<QgsColorRampShader::ColorRampItem> colorRampItemList;
for ( const QgsPointCloudCategory &category : mCategoriesList )
{
QgsColorRampShader::ColorRampItem item( category.value(), category.color(), category.label() );
colorRampItemList.push_back( item );
}
colorRampShader.setColorRampItemList( colorRampItemList );
return colorRampShader;
}
void QgsClassificationPointCloud3DSymbol::fillMaterial( Qt3DRender::QMaterial *mat )
{
QgsColorRampShader mColorRampShader = colorRampShader();
Qt3DRender::QParameter *renderingStyle = new Qt3DRender::QParameter( "u_renderingStyle", QgsPointCloud3DSymbol::ColorRamp );
mat->addParameter( renderingStyle );
Qt3DRender::QParameter *pointSizeParameter = new Qt3DRender::QParameter( "u_pointSize", QVariant::fromValue( mPointSize ) );
mat->addParameter( pointSizeParameter );
// Create the texture to pass the color ramp
Qt3DRender::QTexture1D *colorRampTexture = nullptr;
if ( mColorRampShader.colorRampItemList().count() > 0 )
{
colorRampTexture = new Qt3DRender::QTexture1D( mat );
colorRampTexture->addTextureImage( new QgsColorRampTexture( mColorRampShader, 1 ) );
colorRampTexture->setMinificationFilter( Qt3DRender::QTexture1D::Linear );
colorRampTexture->setMagnificationFilter( Qt3DRender::QTexture1D::Linear );
}
// Parameters
Qt3DRender::QParameter *colorRampTextureParameter = new Qt3DRender::QParameter( "u_colorRampTexture", colorRampTexture );
mat->addParameter( colorRampTextureParameter );
Qt3DRender::QParameter *colorRampCountParameter = new Qt3DRender::QParameter( "u_colorRampCount", mColorRampShader.colorRampItemList().count() );
mat->addParameter( colorRampCountParameter );
int colorRampType = mColorRampShader.colorRampType();
Qt3DRender::QParameter *colorRampTypeParameter = new Qt3DRender::QParameter( "u_colorRampType", colorRampType );
mat->addParameter( colorRampTypeParameter );
}

View File

@ -24,6 +24,7 @@
#include "qgscolorrampshader.h"
#include "qgspointcloudlayer.h"
#include "qgscontrastenhancement.h"
#include "qgspointcloudclassifiedrenderer.h"
/**
* \ingroup 3d
@ -360,4 +361,45 @@ class _3D_EXPORT QgsRgbPointCloud3DSymbol : public QgsPointCloud3DSymbol
};
class _3D_EXPORT QgsClassificationPointCloud3DSymbol : public QgsPointCloud3DSymbol
{
public:
//! Constructor for QgsClassificationPointCloud3DSymbol
QgsClassificationPointCloud3DSymbol();
QgsAbstract3DSymbol *clone() const override SIP_FACTORY;
QString symbolType() const override;
void writeXml( QDomElement &elem, const QgsReadWriteContext &context ) const override;
void readXml( const QDomElement &elem, const QgsReadWriteContext &context ) override;
/**
* Returns the parameter used to select the color of the point cloud
* \see setRenderingParameter( const QString &parameter )
*/
QString renderingParameter() const;
/**
* Sets the parameter used to select the color of the point cloud
* \see renderingParameter()
*/
void setRenderingParameter( const QString &parameter );
QgsPointCloudCategoryList categoriesList() const { return mCategoriesList; }
void setCategoriesList( QgsPointCloudCategoryList categories );
/**
* Returns the color ramp shader used to render the color
* \see setColorRampShader( const QgsColorRampShader &colorRampShader )
*/
QgsColorRampShader colorRampShader() const;
unsigned int byteStride() override { return 4 * sizeof( float ); }
void fillMaterial( Qt3DRender::QMaterial *material ) override SIP_SKIP;
private:
QString mRenderingParameter;
QgsPointCloudCategoryList mCategoriesList;
};
#endif // QGSPOINTCLOUD3DSYMBOL_H

View File

@ -521,4 +521,118 @@ Qt3DRender::QGeometry *QgsRGBPointCloud3DSymbolHandler::makeGeometry( Qt3DCore::
return new QgsRGBPointCloud3DGeometry( parent, data, byteStride );
}
QgsClassificationPointCloud3DSymbolHandler::QgsClassificationPointCloud3DSymbolHandler()
: QgsPointCloud3DSymbolHandler()
{
}
bool QgsClassificationPointCloud3DSymbolHandler::prepare( const QgsPointCloud3DRenderContext &context )
{
Q_UNUSED( context )
return true;
}
void QgsClassificationPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex *pc, const IndexedPointCloudNode &n, const QgsPointCloud3DRenderContext &context )
{
QgsPointCloudAttributeCollection attributes;
constexpr int xOffset = 0;
attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "X" ), QgsPointCloudAttribute::Int32 ) );
const int yOffset = attributes.pointRecordSize();
attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "Y" ), QgsPointCloudAttribute::Int32 ) );
const int zOffset = attributes.pointRecordSize();
attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "Z" ), QgsPointCloudAttribute::Int32 ) );
QString attributeName;
bool attrIsX = false;
bool attrIsY = false;
bool attrIsZ = false;
QgsPointCloudAttribute::DataType attributeType = QgsPointCloudAttribute::Float;
int attributeOffset = 0;
QgsClassificationPointCloud3DSymbol *symbol = dynamic_cast<QgsClassificationPointCloud3DSymbol *>( context.symbol() );
if ( symbol )
{
int offset = 0;
QgsPointCloudAttributeCollection collection = context.attributes();
if ( symbol->renderingParameter() == QLatin1String( "X" ) )
{
attrIsX = true;
}
else if ( symbol->renderingParameter() == QLatin1String( "Y" ) )
{
attrIsY = true;
}
else if ( symbol->renderingParameter() == QLatin1String( "Z" ) )
{
attrIsZ = true;
}
else
{
const QgsPointCloudAttribute *attr = collection.find( symbol->renderingParameter(), offset );
if ( attr )
{
attributeType = attr->type();
attributeName = attr->name();
attributeOffset = attributes.pointRecordSize();
attributes.push_back( *attr );
}
}
}
if ( attributeName.isEmpty() && !attrIsX && !attrIsY && !attrIsZ )
return;
QgsPointCloudRequest request;
request.setAttributes( attributes );
std::unique_ptr<QgsPointCloudBlock> block( pc->nodeData( n, request ) );
if ( !block )
return;
const char *ptr = block->data();
int count = block->pointCount();
const std::size_t recordSize = attributes.pointRecordSize();
const QgsVector3D scale = pc->scale();
const QgsVector3D offset = pc->offset();
for ( int i = 0; i < count; ++i )
{
qint32 ix = *( qint32 * )( ptr + i * recordSize + xOffset );
qint32 iy = *( qint32 * )( ptr + i * recordSize + yOffset );
qint32 iz = *( qint32 * )( ptr + i * recordSize + zOffset );
double x = offset.x() + scale.x() * ix;
double y = offset.y() + scale.y() * iy;
double z = offset.z() + scale.z() * iz;
QVector3D point( x, y, z );
QgsVector3D p = context.map().mapToWorldCoordinates( point );
outNormal.positions.push_back( QVector3D( p.x(), p.y(), p.z() ) );
if ( attrIsX )
outNormal.parameter.push_back( x );
else if ( attrIsY )
outNormal.parameter.push_back( y );
else if ( attrIsZ )
outNormal.parameter.push_back( z );
else
{
float iParam = 0.0f;
context.getAttribute( ptr, i * recordSize + attributeOffset, attributeType, iParam );
outNormal.parameter.push_back( iParam );
}
}
}
void QgsClassificationPointCloud3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const QgsPointCloud3DRenderContext &context )
{
makeEntity( parent, context, outNormal, false );
}
Qt3DRender::QGeometry *QgsClassificationPointCloud3DSymbolHandler::makeGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
{
return new QgsColorRampPointCloud3DGeometry( parent, data, byteStride );
}
/// @endcond

View File

@ -105,6 +105,19 @@ class QgsRGBPointCloud3DSymbolHandler : public QgsPointCloud3DSymbolHandler
Qt3DRender::QGeometry *makeGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride ) override;
};
class QgsClassificationPointCloud3DSymbolHandler : public QgsPointCloud3DSymbolHandler
{
public:
QgsClassificationPointCloud3DSymbolHandler();
bool prepare( const QgsPointCloud3DRenderContext &context ) override;
void processNode( QgsPointCloudIndex *pc, const IndexedPointCloudNode &n, const QgsPointCloud3DRenderContext &context ) override;
void finalize( Qt3DCore::QEntity *parent, const QgsPointCloud3DRenderContext &context ) override;
private:
Qt3DRender::QGeometry *makeGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride ) override;
};
class QgsPointCloud3DGeometry: public Qt3DRender::QGeometry
{
public:

View File

@ -23,6 +23,7 @@
#include "qgspointcloudattributebyramprenderer.h"
#include "qgspointcloudrgbrenderer.h"
#include "qgsdoublevalidator.h"
#include "qgspointcloudclassifiedrendererwidget.h"
QgsPointCloud3DSymbolWidget::QgsPointCloud3DSymbolWidget( QgsPointCloudLayer *layer, QgsPointCloud3DSymbol *symbol, QWidget *parent )
: QWidget( parent )
@ -44,6 +45,7 @@ QgsPointCloud3DSymbolWidget::QgsPointCloud3DSymbolWidget( QgsPointCloudLayer *la
mRenderingStyleComboBox->addItem( tr( "Single Color" ), QStringLiteral( "single-color" ) );
mRenderingStyleComboBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "styleicons/singlebandpseudocolor.svg" ) ), tr( "Attribute by Ramp" ), QStringLiteral( "color-ramp" ) );
mRenderingStyleComboBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "styleicons/multibandcolor.svg" ) ), tr( "RGB" ), QStringLiteral( "rgb" ) );
mRenderingStyleComboBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "styleicons/paletted.svg" ) ), tr( "Classification" ), QStringLiteral( "classification" ) );
connect( mRedMinLineEdit, &QLineEdit::textChanged, this, &QgsPointCloud3DSymbolWidget::mRedMinLineEdit_textChanged );
connect( mRedMaxLineEdit, &QLineEdit::textChanged, this, &QgsPointCloud3DSymbolWidget::mRedMaxLineEdit_textChanged );
@ -100,6 +102,10 @@ QgsPointCloud3DSymbolWidget::QgsPointCloud3DSymbolWidget( QgsPointCloudLayer *la
connect( mMaxScreenErrorSpinBox, qgis::overload<double>::of( &QDoubleSpinBox::valueChanged ), this, &QgsPointCloud3DSymbolWidget::maximumScreenErrorChanged );
rampAttributeChanged();
mClassifiedRenderer = new QgsPointCloudClassifiedRendererWidget( layer, nullptr );
mClassifiedRenderer->setParent( this );
mClassifiedRenderingLayout->addWidget( mClassifiedRenderer );
}
void QgsPointCloud3DSymbolWidget::setSymbol( QgsPointCloud3DSymbol *symbol )
@ -150,6 +156,12 @@ void QgsPointCloud3DSymbolWidget::setSymbol( QgsPointCloud3DSymbol *symbol )
setMinMaxValue( symb->blueContrastEnhancement(), mBlueMinLineEdit, mBlueMaxLineEdit );
mDisableMinMaxWidgetRefresh--;
}
else if ( symbol->symbolType() == QLatin1String( "classification" ) )
{
mStackedWidget->setCurrentIndex( 4 );
QgsClassificationPointCloud3DSymbol *symb = dynamic_cast<QgsClassificationPointCloud3DSymbol *>( symbol );
mClassifiedRenderer->setFromCategories( symb->categoriesList(), symb->renderingParameter() );
}
else
{
mStackedWidget->setCurrentIndex( 0 );
@ -191,6 +203,15 @@ QgsPointCloud3DSymbol *QgsPointCloud3DSymbolWidget::symbol() const
setCustomMinMaxValues( symb );
retSymb = symb;
}
else if ( symbolType == QLatin1String( "classification" ) )
{
QgsClassificationPointCloud3DSymbol *symb = new QgsClassificationPointCloud3DSymbol;
symb->setPointSize( mPointSizeSpinBox->value() );
symb->setRenderingParameter( mClassifiedRenderer->attribute() );
symb->setCategoriesList( mClassifiedRenderer->categoriesList() );
retSymb = symb;
}
return retSymb;
}
@ -386,6 +407,10 @@ void QgsPointCloud3DSymbolWidget::onRenderingStyleChanged()
( void )( renderer2d );
mBlockChangedSignals--;
}
else if ( newSymbolType == QLatin1String( "classification" ) )
{
}
}
emitChangedSignal();

View File

@ -21,6 +21,7 @@
class QgsPointCloudLayer;
class QgsPointCloud3DSymbol;
class QgsPointCloudClassifiedRendererWidget;
class QgsPointCloud3DSymbolWidget : public QWidget, private Ui::QgsPointCloud3DSymbolWidget
{
@ -64,6 +65,7 @@ class QgsPointCloud3DSymbolWidget : public QWidget, private Ui::QgsPointCloud3DS
private:
int mBlockChangedSignals = 0;
int mDisableMinMaxWidgetRefresh = 0;
QgsPointCloudClassifiedRendererWidget *mClassifiedRenderer = nullptr;
QgsPointCloudLayer *mLayer = nullptr;
bool mBlockMinMaxChanged = false;

View File

@ -427,6 +427,16 @@ QgsPointCloudRenderer *QgsPointCloudClassifiedRendererWidget::renderer()
return renderer.release();
}
QgsPointCloudCategoryList QgsPointCloudClassifiedRendererWidget::categoriesList()
{
return mModel->categories();
}
QString QgsPointCloudClassifiedRendererWidget::attribute()
{
return mAttributeComboBox->currentAttribute();
}
void QgsPointCloudClassifiedRendererWidget::emitWidgetChanged()
{
if ( !mBlockChangedSignal )
@ -513,6 +523,14 @@ void QgsPointCloudClassifiedRendererWidget::setFromRenderer( const QgsPointCloud
mBlockChangedSignal = false;
}
void QgsPointCloudClassifiedRendererWidget::setFromCategories( QgsPointCloudCategoryList categories, const QString &attribute )
{
mBlockChangedSignal = false;
mModel->setRendererCategories( categories );
mAttributeComboBox->setAttribute( attribute );
mBlockChangedSignal = false;
}
void QgsPointCloudClassifiedRendererWidget::changeCategorySymbol()
{
const int row = currentCategoryRow();

View File

@ -23,11 +23,13 @@
#include "ui_qgspointcloudclassifiedrendererwidgetbase.h"
#include "qgis_gui.h"
#include "qgsproxystyle.h"
#include "qgspointcloudattribute.h"
class QgsPointCloudLayer;
class QgsStyle;
class QLineEdit;
class QgsPointCloudClassifiedRenderer;
class QgsPointCloud3DLayer3DRenderer;
#ifndef SIP_RUN
@ -94,6 +96,11 @@ class GUI_EXPORT QgsPointCloudClassifiedRendererWidget: public QgsPointCloudRend
static QgsPointCloudRendererWidget *create( QgsPointCloudLayer *layer, QgsStyle *style, QgsPointCloudRenderer * );
QgsPointCloudRenderer *renderer() override;
QgsPointCloudCategoryList categoriesList();
QString attribute();
void setFromCategories( QgsPointCloudCategoryList categories, const QString &attribute );
private slots:
void emitWidgetChanged();

View File

@ -81,7 +81,7 @@
<item row="2" column="0" colspan="3">
<widget class="QStackedWidget" name="mStackedWidget">
<property name="currentIndex">
<number>0</number>
<number>4</number>
</property>
<widget class="QWidget" name="noRendererPage"/>
<widget class="QWidget" name="singleColorRendererPage">
@ -160,7 +160,7 @@
<item row="2" column="0" colspan="2">
<widget class="QgsColorRampShaderWidget" name="mColorRampShaderWidget" native="true"/>
</item>
<item row="1" column="1">
<item row="1" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,1,0,1,0">
<item>
<widget class="QLabel" name="label_3">
@ -446,6 +446,13 @@ enhancement</string>
</item>
</layout>
</widget>
<widget class="QWidget" name="classifiedrenderingPage">
<layout class="QGridLayout" name="gridLayout_6">
<item row="0" column="0">
<layout class="QGridLayout" name="mClassifiedRenderingLayout"/>
</item>
</layout>
</widget>
</widget>
</item>
<item row="1" column="0" colspan="3">