Skip to content

Commit

Permalink
[FEATURE] Data-defined extrusion and height for 3D polygon symbols
Browse files Browse the repository at this point in the history
... because buildings with even heights are boring!
  • Loading branch information
wonder-sk committed Oct 11, 2017
1 parent 6c1d570 commit 5515d68
Show file tree
Hide file tree
Showing 11 changed files with 236 additions and 101 deletions.
10 changes: 5 additions & 5 deletions src/3d/qgstessellatedpolygongeometry.cpp
Expand Up @@ -65,17 +65,17 @@ QgsTessellatedPolygonGeometry::~QgsTessellatedPolygonGeometry()
qDeleteAll( mPolygons );
}

void QgsTessellatedPolygonGeometry::setPolygons( const QList<QgsPolygonV2 *> &polygons, const QgsPointXY &origin, float extrusionHeight )
void QgsTessellatedPolygonGeometry::setPolygons( const QList<QgsPolygonV2 *> &polygons, const QgsPointXY &origin, float extrusionHeight, const QList<float> &extrusionHeightPerPolygon )
{
qDeleteAll( mPolygons );
mPolygons = polygons;

int i = 0;
QgsTessellator tesselator( origin.x(), origin.y(), mWithNormals );
Q_FOREACH ( QgsPolygonV2 *polygon, polygons )
for ( int i = 0; i < polygons.count(); ++i )
{
tesselator.addPolygon( *polygon, extrusionHeight );
++i;
QgsPolygonV2 *polygon = polygons.at( i );
float extr = extrusionHeightPerPolygon.isEmpty() ? extrusionHeight : extrusionHeightPerPolygon.at( i );
tesselator.addPolygon( *polygon, extr );
}

QByteArray data( ( const char * )tesselator.data().constData(), tesselator.data().count() * sizeof( float ) );
Expand Down
2 changes: 1 addition & 1 deletion src/3d/qgstessellatedpolygongeometry.h
Expand Up @@ -42,7 +42,7 @@ class QgsTessellatedPolygonGeometry : public Qt3DRender::QGeometry
~QgsTessellatedPolygonGeometry();

//! Initializes vertex buffer from given polygons. Takes ownership of passed polygon geometries
void setPolygons( const QList<QgsPolygonV2 *> &polygons, const QgsPointXY &origin, float extrusionHeight );
void setPolygons( const QList<QgsPolygonV2 *> &polygons, const QgsPointXY &origin, float extrusionHeight, const QList<float> &extrusionHeightPerPolygon = QList<float>() );

private:
QList<QgsPolygonV2 *> mPolygons;
Expand Down
25 changes: 25 additions & 0 deletions src/3d/symbols/qgsabstract3dsymbol.cpp
Expand Up @@ -14,3 +14,28 @@
***************************************************************************/

#include "qgsabstract3dsymbol.h"


QgsPropertiesDefinition QgsAbstract3DSymbol::sPropertyDefinitions;


const QgsPropertiesDefinition &QgsAbstract3DSymbol::propertyDefinitions()
{
initPropertyDefinitions();
return sPropertyDefinitions;
}

void QgsAbstract3DSymbol::initPropertyDefinitions()
{
if ( !sPropertyDefinitions.isEmpty() )
return;

QString origin = QStringLiteral( "symbol3d" );

sPropertyDefinitions = QgsPropertiesDefinition
{
{ PropertyHeight, QgsPropertyDefinition( "height", QObject::tr( "Height" ), QgsPropertyDefinition::Double, origin ) },
{ PropertyExtrusionHeight, QgsPropertyDefinition( "extrusionHeight", QObject::tr( "ExtrusionHeight" ), QgsPropertyDefinition::DoublePositive, origin ) },
};

}
30 changes: 30 additions & 0 deletions src/3d/symbols/qgsabstract3dsymbol.h
Expand Up @@ -19,6 +19,8 @@
#include "qgis_3d.h"
#include "qgis_sip.h"

#include "qgspropertycollection.h"

class QDomElement;
class QString;

Expand Down Expand Up @@ -47,6 +49,34 @@ class _3D_EXPORT QgsAbstract3DSymbol
virtual void writeXml( QDomElement &elem, const QgsReadWriteContext &context ) const = 0;
//! Reads symbol configuration from the given DOM element
virtual void readXml( const QDomElement &elem, const QgsReadWriteContext &context ) = 0;

//! Data definable properties.
enum Property
{
PropertyHeight = 0, //!< Height (altitude)
PropertyExtrusionHeight, //!< Extrusion height (zero means no extrusion)
};

//! Returns the symbol layer property definitions.
static const QgsPropertiesDefinition &propertyDefinitions();

//! Returns a reference to the symbol layer's property collection, used for data defined overrides.
QgsPropertyCollection &dataDefinedProperties() { return mDataDefinedProperties; }

//! Returns a reference to the symbol layer's property collection, used for data defined overrides.
const QgsPropertyCollection &dataDefinedProperties() const { return mDataDefinedProperties; } SIP_SKIP

//! Sets the symbol layer's property collection, used for data defined overrides.
void setDataDefinedProperties( const QgsPropertyCollection &collection ) { mDataDefinedProperties = collection; }

protected:
QgsPropertyCollection mDataDefinedProperties;

private:
static void initPropertyDefinitions();

//! Property definitions
static QgsPropertiesDefinition sPropertyDefinitions;
};


Expand Down
8 changes: 8 additions & 0 deletions src/3d/symbols/qgspolygon3dsymbol.cpp
Expand Up @@ -44,6 +44,10 @@ void QgsPolygon3DSymbol::writeXml( QDomElement &elem, const QgsReadWriteContext
QDomElement elemMaterial = doc.createElement( QStringLiteral( "material" ) );
mMaterial.writeXml( elemMaterial );
elem.appendChild( elemMaterial );

QDomElement elemDDP = doc.createElement( QStringLiteral( "data-defined-properties" ) );
mDataDefinedProperties.writeXml( elemDDP, propertyDefinitions() );
elem.appendChild( elemDDP );
}

void QgsPolygon3DSymbol::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
Expand All @@ -58,4 +62,8 @@ void QgsPolygon3DSymbol::readXml( const QDomElement &elem, const QgsReadWriteCon

QDomElement elemMaterial = elem.firstChildElement( QStringLiteral( "material" ) );
mMaterial.readXml( elemMaterial );

QDomElement elemDDP = elem.firstChildElement( QStringLiteral( "data-defined-properties" ) );
if ( !elemDDP.isNull() )
mDataDefinedProperties.readXml( elemDDP, propertyDefinitions() );
}
48 changes: 43 additions & 5 deletions src/3d/symbols/qgspolygon3dsymbol_p.cpp
Expand Up @@ -26,6 +26,23 @@
#include "qgsmultipolygon.h"


static QgsExpressionContext _expressionContext3D()
{
QgsExpressionContext ctx;
ctx << QgsExpressionContextUtils::globalScope()
<< QgsExpressionContextUtils::projectScope( QgsProject::instance() );
return ctx;
}

static QSet<QString> _requiredAttributes( const QgsPolygon3DSymbol &symbol, QgsVectorLayer *layer )
{
QgsExpressionContext ctx( _expressionContext3D() );
ctx.setFields( layer->fields() );
//symbol.dataDefinedProperties().prepare( ctx );
return symbol.dataDefinedProperties().referencedFields( ctx );
}


/// @cond PRIVATE

QgsPolygon3DSymbolEntity::QgsPolygon3DSymbolEntity( const Qgs3DMapSettings &map, QgsVectorLayer *layer, const QgsPolygon3DSymbol &symbol, Qt3DCore::QNode *parent )
Expand All @@ -51,7 +68,7 @@ void QgsPolygon3DSymbolEntity::addEntityForSelectedPolygons( const Qgs3DMapSetti
// build the feature request to select features
QgsFeatureRequest req;
req.setDestinationCrs( map.crs() );
req.setSubsetOfAttributes( QgsAttributeList() );
req.setSubsetOfAttributes( _requiredAttributes( symbol, layer ), layer->fields() );
req.setFilterFids( layer->selectedFeatureIds() );

// build the entity
Expand All @@ -72,7 +89,7 @@ void QgsPolygon3DSymbolEntity::addEntityForNotSelectedPolygons( const Qgs3DMapSe

// build the feature request to select features
QgsFeatureRequest req;
req.setSubsetOfAttributes( QgsAttributeList() );
req.setSubsetOfAttributes( _requiredAttributes( symbol, layer ), layer->fields() );
req.setDestinationCrs( map.crs() );

QgsFeatureIds notSelected = layer->allFeatureIds();
Expand Down Expand Up @@ -106,6 +123,15 @@ Qt3DRender::QGeometryRenderer *QgsPolygon3DSymbolEntityNode::renderer( const Qgs
{
QgsPointXY origin( map.originX(), map.originY() );
QList<QgsPolygonV2 *> polygons;
QList<float> extrusionHeightPerPolygon; // will stay empty if not needed per polygon

QgsExpressionContext ctx( _expressionContext3D() );
ctx.setFields( layer->fields() );

const QgsPropertyCollection &ddp = symbol.dataDefinedProperties();
bool hasDDHeight = ddp.isActive( QgsAbstract3DSymbol::PropertyHeight );
bool hasDDExtrusion = ddp.isActive( QgsAbstract3DSymbol::PropertyExtrusionHeight );

QgsFeature f;
QgsFeatureIterator fi = layer->getFeatures( request );
while ( fi.nextFeature( f ) )
Expand All @@ -121,11 +147,21 @@ Qt3DRender::QGeometryRenderer *QgsPolygon3DSymbolEntityNode::renderer( const Qgs

const QgsAbstractGeometry *g = geom.geometry();

ctx.setFeature( f );
float height = symbol.height();
float extrusionHeight = symbol.extrusionHeight();
if ( hasDDHeight )
height = ddp.valueAsDouble( QgsAbstract3DSymbol::PropertyHeight, ctx, height );
if ( hasDDExtrusion )
extrusionHeight = ddp.valueAsDouble( QgsAbstract3DSymbol::PropertyExtrusionHeight, ctx, extrusionHeight );

if ( const QgsPolygonV2 *poly = qgsgeometry_cast< const QgsPolygonV2 *>( g ) )
{
QgsPolygonV2 *polyClone = poly->clone();
Qgs3DUtils::clampAltitudes( polyClone, symbol.altitudeClamping(), symbol.altitudeBinding(), symbol.height(), map );
Qgs3DUtils::clampAltitudes( polyClone, symbol.altitudeClamping(), symbol.altitudeBinding(), height, map );
polygons.append( polyClone );
if ( hasDDExtrusion )
extrusionHeightPerPolygon.append( extrusionHeight );
}
else if ( const QgsMultiPolygonV2 *mpoly = qgsgeometry_cast< const QgsMultiPolygonV2 *>( g ) )
{
Expand All @@ -134,16 +170,18 @@ Qt3DRender::QGeometryRenderer *QgsPolygon3DSymbolEntityNode::renderer( const Qgs
const QgsAbstractGeometry *g2 = mpoly->geometryN( i );
Q_ASSERT( QgsWkbTypes::flatType( g2->wkbType() ) == QgsWkbTypes::Polygon );
QgsPolygonV2 *polyClone = static_cast< const QgsPolygonV2 *>( g2 )->clone();
Qgs3DUtils::clampAltitudes( polyClone, symbol.altitudeClamping(), symbol.altitudeBinding(), symbol.height(), map );
Qgs3DUtils::clampAltitudes( polyClone, symbol.altitudeClamping(), symbol.altitudeBinding(), height, map );
polygons.append( polyClone );
if ( hasDDExtrusion )
extrusionHeightPerPolygon.append( extrusionHeight );
}
}
else
qDebug() << "not a polygon";
}

mGeometry = new QgsTessellatedPolygonGeometry;
mGeometry->setPolygons( polygons, origin, symbol.extrusionHeight() );
mGeometry->setPolygons( polygons, origin, symbol.extrusionHeight(), extrusionHeightPerPolygon );

Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
renderer->setGeometry( mGeometry );
Expand Down
15 changes: 13 additions & 2 deletions src/app/3d/qgspolygon3dsymbolwidget.cpp
Expand Up @@ -23,22 +23,27 @@ QgsPolygon3DSymbolWidget::QgsPolygon3DSymbolWidget( QWidget *parent )
{
setupUi( this );

setSymbol( QgsPolygon3DSymbol() );
setSymbol( QgsPolygon3DSymbol(), nullptr );

connect( spinHeight, static_cast<void ( QDoubleSpinBox::* )( double )>( &QDoubleSpinBox::valueChanged ), this, &QgsPolygon3DSymbolWidget::changed );
connect( spinExtrusion, static_cast<void ( QDoubleSpinBox::* )( double )>( &QDoubleSpinBox::valueChanged ), this, &QgsPolygon3DSymbolWidget::changed );
connect( cboAltClamping, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsPolygon3DSymbolWidget::changed );
connect( cboAltBinding, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsPolygon3DSymbolWidget::changed );
connect( widgetMaterial, &QgsPhongMaterialWidget::changed, this, &QgsPolygon3DSymbolWidget::changed );
connect( btnHeightDD, &QgsPropertyOverrideButton::changed, this, &QgsPolygon3DSymbolWidget::changed );
connect( btnExtrusionDD, &QgsPropertyOverrideButton::changed, this, &QgsPolygon3DSymbolWidget::changed );
}

void QgsPolygon3DSymbolWidget::setSymbol( const QgsPolygon3DSymbol &symbol )
void QgsPolygon3DSymbolWidget::setSymbol( const QgsPolygon3DSymbol &symbol, QgsVectorLayer *layer )
{
spinHeight->setValue( symbol.height() );
spinExtrusion->setValue( symbol.extrusionHeight() );
cboAltClamping->setCurrentIndex( ( int ) symbol.altitudeClamping() );
cboAltBinding->setCurrentIndex( ( int ) symbol.altitudeBinding() );
widgetMaterial->setMaterial( symbol.material() );

btnHeightDD->init( QgsAbstract3DSymbol::PropertyHeight, symbol.dataDefinedProperties(), QgsAbstract3DSymbol::propertyDefinitions(), layer, true );
btnExtrusionDD->init( QgsAbstract3DSymbol::PropertyExtrusionHeight, symbol.dataDefinedProperties(), QgsAbstract3DSymbol::propertyDefinitions(), layer, true );
}

QgsPolygon3DSymbol QgsPolygon3DSymbolWidget::symbol() const
Expand All @@ -49,5 +54,11 @@ QgsPolygon3DSymbol QgsPolygon3DSymbolWidget::symbol() const
sym.setAltitudeClamping( ( AltitudeClamping ) cboAltClamping->currentIndex() );
sym.setAltitudeBinding( ( AltitudeBinding ) cboAltBinding->currentIndex() );
sym.setMaterial( widgetMaterial->material() );

QgsPropertyCollection ddp;
ddp.setProperty( QgsAbstract3DSymbol::PropertyHeight, btnHeightDD->toProperty() );
ddp.setProperty( QgsAbstract3DSymbol::PropertyExtrusionHeight, btnExtrusionDD->toProperty() );
sym.setDataDefinedProperties( ddp );

return sym;
}
2 changes: 1 addition & 1 deletion src/app/3d/qgspolygon3dsymbolwidget.h
Expand Up @@ -29,7 +29,7 @@ class QgsPolygon3DSymbolWidget : public QWidget, private Ui::Polygon3DSymbolWidg
public:
explicit QgsPolygon3DSymbolWidget( QWidget *parent = nullptr );

void setSymbol( const QgsPolygon3DSymbol &symbol );
void setSymbol( const QgsPolygon3DSymbol &symbol, QgsVectorLayer *layer );
QgsPolygon3DSymbol symbol() const;

signals:
Expand Down
4 changes: 2 additions & 2 deletions src/app/3d/qgsvectorlayer3drendererwidget.cpp
Expand Up @@ -118,11 +118,11 @@ void QgsVectorLayer3DRendererWidget::setRenderer( const QgsVectorLayer3DRenderer
pageIndex = 3;
if ( mRenderer && mRenderer->symbol() && mRenderer->symbol()->type() == "polygon" )
{
whileBlocking( widgetPolygon )->setSymbol( *static_cast<const QgsPolygon3DSymbol *>( mRenderer->symbol() ) );
whileBlocking( widgetPolygon )->setSymbol( *static_cast<const QgsPolygon3DSymbol *>( mRenderer->symbol() ), vlayer );
}
else
{
whileBlocking( widgetPolygon )->setSymbol( QgsPolygon3DSymbol() );
whileBlocking( widgetPolygon )->setSymbol( QgsPolygon3DSymbol(), vlayer );
}
break;

Expand Down
2 changes: 1 addition & 1 deletion src/gui/qgspropertyoverridebutton.cpp
Expand Up @@ -335,7 +335,7 @@ void QgsPropertyOverrideButton::aboutToShowMenu()
mDefineMenu->addSeparator();

// deactivate button if field already exists
if ( mAuxiliaryStorageEnabled )
if ( mAuxiliaryStorageEnabled && mVectorLayer )
{
mDefineMenu->addAction( mActionCreateAuxiliaryField );

Expand Down

2 comments on commit 5515d68

@nirvn
Copy link
Contributor

@nirvn nirvn commented on 5515d68 Oct 12, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

!!

@NathanW2
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WIN!!!

Please sign in to comment.