Add QgsMapLayerElevationProperties subclass for vector layers

Allows elevation properties to be set for vector layers, including:

- altitude binding
- altitude clamping
- extrusion
- scale
- offset

These properties can be set through the new "Elevation" tab in
the vector layer properties dialog
This commit is contained in:
Nyall Dawson 2022-02-24 12:08:42 +10:00
parent 5395d87d7f
commit cc24106d01
23 changed files with 967 additions and 212 deletions

View File

@ -39,64 +39,6 @@ Constructor for QgsPointCloudLayerElevationProperties, with the specified ``pare
virtual QgsDoubleRange calculateZRange( QgsMapLayer *layer ) const;
double zOffset() const;
%Docstring
Returns the z offset, which is a fixed offset amount which should be added to z values from
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
void setZOffset( double offset );
%Docstring
Sets the z ``offset``, which is a fixed offset amount which will be added to z values from
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,12 +26,17 @@ how an individual :py:class:`QgsMapLayer` behaves with relation to z values or e
#include "qgsmaplayerelevationproperties.h"
#include "qgspointcloudlayerelevationproperties.h"
#include "qgsrasterlayerelevationproperties.h"
#include "qgsvectorlayerelevationproperties.h"
%End
%ConvertToSubClassCode
if ( qobject_cast<QgsPointCloudLayerElevationProperties *>( sipCpp ) )
{
sipType = sipType_QgsPointCloudLayerElevationProperties;
}
else if ( qobject_cast<QgsVectorLayerElevationProperties *>( sipCpp ) )
{
sipType = sipType_QgsVectorLayerElevationProperties;
}
else if ( qobject_cast<QgsRasterLayerElevationProperties *>( sipCpp ) )
{
sipType = sipType_QgsRasterLayerElevationProperties;
@ -90,6 +95,60 @@ Attempts to calculate the overall elevation or z range for the specified ``layer
the settings defined by this elevation properties object.
May return an infinite range if the extent could not be calculated.
%End
double zOffset() const;
%Docstring
Returns the z offset, which is a fixed offset amount which should be added to z values from
the layer.
.. note::
Any scaling specified via :py:func:`~QgsMapLayerElevationProperties.zScale` is applied before any offset value specified via :py:func:`~QgsMapLayerElevationProperties.zOffset`
.. seealso:: :py:func:`setZOffset`
%End
void setZOffset( double offset );
%Docstring
Sets the z ``offset``, which is a fixed offset amount which will be added to z values from
the layer.
.. note::
Any scaling specified via :py:func:`~QgsMapLayerElevationProperties.zScale` is applied before any offset value specified via :py:func:`~QgsMapLayerElevationProperties.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 layer, such
as conversion of elevation values in feet to meters.
.. note::
Any scaling specified via :py:func:`~QgsMapLayerElevationProperties.zScale` is applied before any offset value specified via :py:func:`~QgsMapLayerElevationProperties.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 layer, such
as conversion of elevation values in feet to meters.
.. note::
Any scaling specified via :py:func:`~QgsMapLayerElevationProperties.zScale` is applied before any offset value specified via :py:func:`~QgsMapLayerElevationProperties.zOffset`
.. seealso:: :py:func:`zScale`
%End
signals:
@ -98,6 +157,8 @@ May return an infinite range if the extent could not be calculated.
%Docstring
Emitted when the elevation properties have changed.
%End
protected:
};
/************************************************************************

View File

@ -51,54 +51,6 @@ Returns ``True`` if the elevation properties are enabled, i.e. the raster layer
Sets whether the elevation properties are enabled, i.e. the raster layer values represent an elevation surface.
.. seealso:: :py:func:`isEnabled`
%End
double zOffset() const;
%Docstring
Returns the z offset, which is a fixed offset amount which should be added to z values from
the layer.
.. note::
Any scaling specified via :py:func:`~QgsRasterLayerElevationProperties.zScale` is applied before any offset value specified via :py:func:`~QgsRasterLayerElevationProperties.zOffset`
.. seealso:: :py:func:`setZOffset`
%End
void setZOffset( double offset );
%Docstring
Sets the z ``offset``, which is a fixed offset amount which will be added to z values from
the layer.
.. note::
Any scaling specified via :py:func:`~QgsRasterLayerElevationProperties.zScale` is applied before any offset value specified via :py:func:`~QgsRasterLayerElevationProperties.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.
.. note::
Any scaling specified via :py:func:`~QgsRasterLayerElevationProperties.zScale` is applied before any offset value specified via :py:func:`~QgsRasterLayerElevationProperties.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.
.. note::
Any scaling specified via :py:func:`~QgsRasterLayerElevationProperties.zScale` is applied before any offset value specified via :py:func:`~QgsRasterLayerElevationProperties.zOffset`
.. seealso:: :py:func:`zScale`
%End
};

View File

@ -509,12 +509,10 @@ Uses :py:class:`QgsExpression`
virtual QgsVectorDataProvider *dataProvider() ${SIP_FINAL};
virtual QgsMapLayerTemporalProperties *temporalProperties();
%Docstring
Returns temporal properties associated with the vector layer.
%End
virtual QgsMapLayerElevationProperties *elevationProperties();
void setProviderEncoding( const QString &encoding );
%Docstring

View File

@ -0,0 +1,102 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/core/vector/qgsvectorlayerelevationproperties.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/
class QgsVectorLayerElevationProperties : QgsMapLayerElevationProperties
{
%Docstring(signature="appended")
Vector layer specific subclass of :py:class:`QgsMapLayerElevationProperties`.
.. versionadded:: 3.26
%End
%TypeHeaderCode
#include "qgsvectorlayerelevationproperties.h"
%End
public:
QgsVectorLayerElevationProperties( QObject *parent /TransferThis/ );
%Docstring
Constructor for QgsVectorLayerElevationProperties, with the specified ``parent`` object.
%End
virtual bool hasElevation() const;
virtual QDomElement writeXml( QDomElement &element, QDomDocument &doc, const QgsReadWriteContext &context );
virtual bool readXml( const QDomElement &element, const QgsReadWriteContext &context );
virtual bool isVisibleInZRange( const QgsDoubleRange &range ) const;
virtual QgsDoubleRange calculateZRange( QgsMapLayer *layer ) const;
Qgis::AltitudeClamping clamping() const;
%Docstring
Returns the altitude clamping method, which dictates how feature heights are interpreted
with respect to terrain heights.
.. seealso:: :py:func:`setClamping`
%End
void setClamping( Qgis::AltitudeClamping clamping );
%Docstring
Sets the altitude ``clamping`` method, which dictates how feature heights are interpreted
with respect to terrain heights.
.. seealso:: :py:func:`clamping`
%End
Qgis::AltitudeBinding binding() const;
%Docstring
Returns the altitude binding method, which determines how altitude is bound to individual vertices in features.
.. seealso:: :py:func:`setBinding`
%End
void setBinding( Qgis::AltitudeBinding binding );
%Docstring
Sets the altitude ``binding`` method, which determines how altitude is bound to individual vertices in features.
.. seealso:: :py:func:`binding`
%End
double extrusionHeight() const;
%Docstring
Returns the feature extrusion height.
.. note::
the :py:func:`~QgsVectorLayerElevationProperties.zScale` factor is NOT applied to extrusion heights.
.. seealso:: :py:func:`setExtrusionHeight`
%End
void setExtrusionHeight( double height );
%Docstring
Sets the feature extrusion height.
.. note::
the :py:func:`~QgsVectorLayerElevationProperties.zScale` factor is NOT applied to extrusion heights.
.. seealso:: :py:func:`extrusionHeight`
%End
};
/************************************************************************
* This file has been generated automatically from *
* *
* src/core/vector/qgsvectorlayerelevationproperties.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/

View File

@ -674,6 +674,7 @@
%Include auto_generated/vector/qgsvectorlayereditbuffer.sip
%Include auto_generated/vector/qgsvectorlayereditpassthrough.sip
%Include auto_generated/vector/qgsvectorlayereditutils.sip
%Include auto_generated/vector/qgsvectorlayerelevationproperties.sip
%Include auto_generated/vector/qgsvectorlayerexporter.sip
%Include auto_generated/vector/qgsvectorlayerfeaturecounter.sip
%Include auto_generated/vector/qgsvectorlayerfeatureiterator.sip

View File

@ -259,6 +259,8 @@ set(QGIS_APP_SRCS
mesh/qgsmaptooleditmeshframe.cpp
mesh/qgsmeshtransformcoordinatesdockwidget.cpp
mesh/qgsmeshselectbyexpressiondialog.cpp
vector/qgsvectorelevationpropertieswidget.cpp
)
if (WITH_SPATIALITE)

View File

@ -118,6 +118,7 @@
#include "options/qgscustomprojectionoptions.h"
#include "raster/qgsrasterelevationpropertieswidget.h"
#include "vector/qgsvectorelevationpropertieswidget.h"
#ifdef HAVE_3D
#include "qgs3d.h"
@ -1481,6 +1482,7 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipBadLayers
#endif
registerMapLayerPropertiesFactory( new QgsPointCloudElevationPropertiesWidgetFactory( this ) );
registerMapLayerPropertiesFactory( new QgsRasterElevationPropertiesWidgetFactory( this ) );
registerMapLayerPropertiesFactory( new QgsVectorElevationPropertiesWidgetFactory( this ) );
registerMapLayerPropertiesFactory( new QgsAnnotationItemPropertiesWidgetFactory( this ) );
registerMapLayerPropertiesFactory( new QgsLayerTreeGroupPropertiesWidgetFactory( this ) );

View File

@ -92,7 +92,7 @@ bool QgsRasterElevationPropertiesWidgetFactory::supportLayerPropertiesDialog() c
bool QgsRasterElevationPropertiesWidgetFactory::supportsStyleDock() const
{
return true;
return false;
}
bool QgsRasterElevationPropertiesWidgetFactory::supportsLayer( QgsMapLayer *layer ) const

View File

@ -0,0 +1,186 @@
/***************************************************************************
qgsvectorrelevationpropertieswidget.cpp
---------------------
begin : February 2022
copyright : (C) 2022 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* 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. *
* *
***************************************************************************/
#include "qgsvectorelevationpropertieswidget.h"
#include "qgsstyle.h"
#include "qgsapplication.h"
#include "qgsmaplayer.h"
#include "qgsvectorlayer.h"
#include "qgsvectorlayerelevationproperties.h"
QgsVectorElevationPropertiesWidget::QgsVectorElevationPropertiesWidget( QgsVectorLayer *layer, QgsMapCanvas *canvas, QWidget *parent )
: QgsMapLayerConfigWidget( layer, canvas, parent )
{
setupUi( this );
mOffsetZSpinBox->setClearValue( 0 );
mScaleZSpinBox->setClearValue( 1 );
mExtrusionSpinBox->setClearValue( 0 );
mComboClamping->addItem( tr( "Clamped to Terrain" ), static_cast< int >( Qgis::AltitudeClamping::Terrain ) );
mComboClamping->addItem( tr( "Relative to Terrain" ), static_cast< int >( Qgis::AltitudeClamping::Relative ) );
mComboClamping->addItem( tr( "Absolute" ), static_cast< int >( Qgis::AltitudeClamping::Absolute ) );
mComboBinding->addItem( tr( "Vertex" ), static_cast< int >( Qgis::AltitudeBinding::Vertex ) );
mComboBinding->addItem( tr( "Centroid" ), static_cast< int >( Qgis::AltitudeBinding::Centroid ) );
syncToLayer( layer );
connect( mOffsetZSpinBox, qOverload<double >( &QDoubleSpinBox::valueChanged ), this, &QgsVectorElevationPropertiesWidget::onChanged );
connect( mScaleZSpinBox, qOverload<double >( &QDoubleSpinBox::valueChanged ), this, &QgsVectorElevationPropertiesWidget::onChanged );
connect( mExtrusionSpinBox, qOverload<double >( &QDoubleSpinBox::valueChanged ), this, &QgsVectorElevationPropertiesWidget::onChanged );
connect( mComboClamping, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsVectorElevationPropertiesWidget::onChanged );
connect( mComboBinding, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsVectorElevationPropertiesWidget::onChanged );
connect( mComboClamping, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsVectorElevationPropertiesWidget::clampingChanged );
connect( mComboBinding, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsVectorElevationPropertiesWidget::bindingChanged );
}
void QgsVectorElevationPropertiesWidget::syncToLayer( QgsMapLayer *layer )
{
mLayer = qobject_cast< QgsVectorLayer * >( layer );
if ( !mLayer )
return;
mBlockUpdates = true;
const QgsVectorLayerElevationProperties *props = qgis::down_cast< const QgsVectorLayerElevationProperties * >( mLayer->elevationProperties() );
mComboClamping->setCurrentIndex( mComboClamping->findData( static_cast< int >( props->clamping() ) ) );
mComboBinding->setCurrentIndex( mComboBinding->findData( static_cast< int >( props->binding() ) ) );
mOffsetZSpinBox->setValue( props->zOffset() );
mScaleZSpinBox->setValue( props->zScale() );
mExtrusionSpinBox->setValue( props->extrusionHeight() );
mBlockUpdates = false;
clampingChanged();
bindingChanged();
}
void QgsVectorElevationPropertiesWidget::apply()
{
if ( !mLayer )
return;
QgsVectorLayerElevationProperties *props = qgis::down_cast< QgsVectorLayerElevationProperties * >( mLayer->elevationProperties() );
props->setZOffset( mOffsetZSpinBox->value() );
props->setZScale( mScaleZSpinBox->value() );
props->setClamping( static_cast< Qgis::AltitudeClamping >( mComboClamping->currentData().toInt() ) );
props->setBinding( static_cast< Qgis::AltitudeBinding >( mComboBinding->currentData().toInt() ) );
props->setExtrusionHeight( mExtrusionSpinBox->value() );
mLayer->trigger3DUpdate();
}
void QgsVectorElevationPropertiesWidget::onChanged()
{
if ( !mBlockUpdates )
emit widgetChanged();
}
void QgsVectorElevationPropertiesWidget::clampingChanged()
{
bool enableScale = true;
bool enableBinding = true;
switch ( static_cast< Qgis::AltitudeClamping >( mComboClamping->currentData().toInt() ) )
{
case Qgis::AltitudeClamping::Absolute:
mLabelClampingExplanation->setText(
QStringLiteral( "<p><b>%1</b></p><p>%2</p>" ).arg(
tr( "Elevation will be taken directly from features." ),
tr( "Z values from the features will be used for elevation, and the terrain height will be ignored." ) )
);
enableBinding = false; // not used in absolute mode
break;
case Qgis::AltitudeClamping::Relative:
mLabelClampingExplanation->setText(
QStringLiteral( "<p><b>%1</b></p><p>%2</p>" ).arg(
tr( "Elevation is relative to terrain height." ),
tr( "Any z values present in the features will be added to the terrain height." ) )
);
break;
case Qgis::AltitudeClamping::Terrain:
mLabelClampingExplanation->setText(
QStringLiteral( "<p><b>%1</b></p><p>%2</p>" ).arg(
tr( "Feature elevation will be taken directly from the terrain height." ),
tr( "Any existing z values present in the features will be ignored." ) )
);
enableScale = false; // not used in terrain mode
break;
}
mLabelScale->setVisible( enableScale );
mScaleZSpinBox->setVisible( enableScale );
mBindingGroupBox->setVisible( enableBinding );
}
void QgsVectorElevationPropertiesWidget::bindingChanged()
{
switch ( static_cast< Qgis::AltitudeBinding >( mComboBinding->currentData().toInt() ) )
{
case Qgis::AltitudeBinding::Vertex:
mLabelBindingExplanation->setText(
QStringLiteral( "<p><b>%1</b></p><p>%2</p>" ).arg(
tr( "Feature elevation is relative to the terrain height at every vertex." ),
tr( "The terrain will be sampled at every individual vertex before being added to the vertex's z value." )
)
);
break;
case Qgis::AltitudeBinding::Centroid:
mLabelBindingExplanation->setText(
QStringLiteral( "<p><b>%1</b></p><p>%2</p>" ).arg(
tr( "Feature elevation is relative to the terrain height at feature's centroid only." ),
tr( "The terrain will be sampled once at the feature's centroid, with the centroid height being added to each vertex's z value." )
)
);
break;
}
}
//
// QgsVectorElevationPropertiesWidgetFactory
//
QgsVectorElevationPropertiesWidgetFactory::QgsVectorElevationPropertiesWidgetFactory( QObject *parent )
: QObject( parent )
{
setIcon( QgsApplication::getThemeIcon( QStringLiteral( "propertyicons/elevationscale.svg" ) ) );
setTitle( tr( "Elevation" ) );
}
QgsMapLayerConfigWidget *QgsVectorElevationPropertiesWidgetFactory::createWidget( QgsMapLayer *layer, QgsMapCanvas *canvas, bool, QWidget *parent ) const
{
return new QgsVectorElevationPropertiesWidget( qobject_cast< QgsVectorLayer * >( layer ), canvas, parent );
}
bool QgsVectorElevationPropertiesWidgetFactory::supportLayerPropertiesDialog() const
{
return true;
}
bool QgsVectorElevationPropertiesWidgetFactory::supportsStyleDock() const
{
return false;
}
bool QgsVectorElevationPropertiesWidgetFactory::supportsLayer( QgsMapLayer *layer ) const
{
return layer->type() == QgsMapLayerType::VectorLayer;
}
QString QgsVectorElevationPropertiesWidgetFactory::layerPropertiesPagePositionHint() const
{
return QStringLiteral( "mOptsPage_Metadata" );
}

View File

@ -0,0 +1,67 @@
/***************************************************************************
qgsvectorrelevationpropertieswidget.h
---------------------
begin : February 2022
copyright : (C) 2022 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* 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. *
* *
***************************************************************************/
#ifndef QGSVECTORELEVATIONPROPERTIESWIDGET_H
#define QGSVECTORELEVATIONPROPERTIESWIDGET_H
#include "qgsmaplayerconfigwidget.h"
#include "qgsmaplayerconfigwidgetfactory.h"
#include "ui_qgsvectorelevationpropertieswidgetbase.h"
class QgsVectorLayer;
class QgsVectorElevationPropertiesWidget : public QgsMapLayerConfigWidget, private Ui::QgsVectorElevationPropertiesWidgetBase
{
Q_OBJECT
public:
QgsVectorElevationPropertiesWidget( QgsVectorLayer *layer, QgsMapCanvas *canvas, QWidget *parent );
void syncToLayer( QgsMapLayer *layer ) override;
public slots:
void apply() override;
private slots:
void onChanged();
void clampingChanged();
void bindingChanged();
private:
QgsVectorLayer *mLayer = nullptr;
bool mBlockUpdates = false;
};
class QgsVectorElevationPropertiesWidgetFactory : public QObject, public QgsMapLayerConfigWidgetFactory
{
Q_OBJECT
public:
explicit QgsVectorElevationPropertiesWidgetFactory( QObject *parent = nullptr );
QgsMapLayerConfigWidget *createWidget( QgsMapLayer *layer, QgsMapCanvas *canvas, bool dockWidget, QWidget *parent ) const override;
bool supportLayerPropertiesDialog() const override;
bool supportsStyleDock() const override;
bool supportsLayer( QgsMapLayer *layer ) const override;
QString layerPropertiesPagePositionHint() const override;
};
#endif // QGSVECTORELEVATIONPROPERTIESWIDGET_H

View File

@ -819,6 +819,7 @@ set(QGIS_CORE_SRCS
vector/qgsvectorlayereditbuffer.cpp
vector/qgsvectorlayereditpassthrough.cpp
vector/qgsvectorlayereditutils.cpp
vector/qgsvectorlayerelevationproperties.cpp
vector/qgsvectorlayerfeatureiterator.cpp
vector/qgsvectorlayerexporter.cpp
vector/qgsvectorlayerjoinbuffer.cpp
@ -1758,6 +1759,7 @@ set(QGIS_CORE_HDRS
vector/qgsvectorlayereditbuffer.h
vector/qgsvectorlayereditpassthrough.h
vector/qgsvectorlayereditutils.h
vector/qgsvectorlayerelevationproperties.h
vector/qgsvectorlayerexporter.h
vector/qgsvectorlayerfeaturecounter.h
vector/qgsvectorlayerfeatureiterator.h

View File

@ -48,60 +48,6 @@ class CORE_EXPORT QgsPointCloudLayerElevationProperties : public QgsMapLayerElev
bool isVisibleInZRange( const QgsDoubleRange &range ) const override;
QgsDoubleRange calculateZRange( QgsMapLayer *layer ) const override;
/**
* Returns the z offset, which is a fixed offset amount which should be added to z values from
* 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 zScale() is applied before any offset value specified via zOffset()
*
* \see setZOffset()
*/
double zOffset() const { return mZOffset; }
/**
* Sets the z \a offset, which is a fixed offset amount which will be added to z values from
* 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 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;
};
#endif // QGSPOINTCLOUDLAYERELEVATIONPROPERTIES_H

View File

@ -42,6 +42,7 @@ class CORE_EXPORT QgsMapLayerElevationProperties : public QObject
#ifdef SIP_RUN
#include "qgspointcloudlayerelevationproperties.h"
#include "qgsrasterlayerelevationproperties.h"
#include "qgsvectorlayerelevationproperties.h"
#endif
Q_OBJECT
@ -52,6 +53,10 @@ class CORE_EXPORT QgsMapLayerElevationProperties : public QObject
{
sipType = sipType_QgsPointCloudLayerElevationProperties;
}
else if ( qobject_cast<QgsVectorLayerElevationProperties *>( sipCpp ) )
{
sipType = sipType_QgsVectorLayerElevationProperties;
}
else if ( qobject_cast<QgsRasterLayerElevationProperties *>( sipCpp ) )
{
sipType = sipType_QgsRasterLayerElevationProperties;
@ -116,12 +121,64 @@ class CORE_EXPORT QgsMapLayerElevationProperties : public QObject
*/
virtual QgsDoubleRange calculateZRange( QgsMapLayer *layer ) const;
/**
* Returns the z offset, which is a fixed offset amount which should be added to z values from
* the layer.
*
* \note Any scaling specified via zScale() is applied before any offset value specified via zOffset()
*
* \see setZOffset()
*/
double zOffset() const { return mZOffset; }
/**
* Sets the z \a offset, which is a fixed offset amount which will be added to z values from
* the 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 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 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; }
signals:
/**
* Emitted when the elevation properties have changed.
*/
void changed();
protected:
//! Z scale
double mZScale = 1.0;
//! Z offset
double mZOffset = 0.0;
};
#endif // QGSMAPLAYERELEVATIONPROPERTIES_H

View File

@ -62,51 +62,10 @@ class CORE_EXPORT QgsRasterLayerElevationProperties : public QgsMapLayerElevatio
*/
void setEnabled( bool enabled ) { mEnabled = enabled; }
/**
* Returns the z offset, which is a fixed offset amount which should be added to z values from
* the layer.
*
* \note Any scaling specified via zScale() is applied before any offset value specified via zOffset()
*
* \see setZOffset()
*/
double zOffset() const { return mZOffset; }
/**
* Sets the z \a offset, which is a fixed offset amount which will be added to z values from
* the 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.
*
* \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.
*
* \note Any scaling specified via zScale() is applied before any offset value specified via zOffset()
*
* \see zScale()
*/
void setZScale( double scale ) { mZScale = scale; }
private:
bool mEnabled = false;
double mZScale = 1.0;
double mZOffset = 0.0;
};
#endif // QGSRASTERLAYERELEVATIONPROPERTIES_H

View File

@ -57,6 +57,7 @@
#include "qgsrendercontext.h"
#include "qgsvectordataprovider.h"
#include "qgsvectorlayertemporalproperties.h"
#include "qgsvectorlayerelevationproperties.h"
#include "qgsvectorlayereditbuffer.h"
#include "qgsvectorlayereditpassthrough.h"
#include "qgsvectorlayereditutils.h"
@ -157,6 +158,7 @@ QgsVectorLayer::QgsVectorLayer( const QString &vectorLayerPath,
const QgsVectorLayer::LayerOptions &options )
: QgsMapLayer( QgsMapLayerType::VectorLayer, baseName, vectorLayerPath )
, mTemporalProperties( new QgsVectorLayerTemporalProperties( this ) )
, mElevationProperties( new QgsVectorLayerElevationProperties( this ) )
, mAuxiliaryLayer( nullptr )
, mAuxiliaryLayerKey( QString() )
, mReadExtentFromXml( options.readExtentFromXml )
@ -670,6 +672,11 @@ QgsMapLayerTemporalProperties *QgsVectorLayer::temporalProperties()
return mTemporalProperties;
}
QgsMapLayerElevationProperties *QgsVectorLayer::elevationProperties()
{
return mElevationProperties;
}
void QgsVectorLayer::setProviderEncoding( const QString &encoding )
{
if ( isValid() && mDataProvider && mDataProvider->encoding() != encoding )

View File

@ -80,6 +80,7 @@ class QgsGeometryOptions;
class QgsStyleEntityVisitorInterface;
class QgsVectorLayerTemporalProperties;
class QgsFeatureRendererGenerator;
class QgsVectorLayerElevationProperties;
typedef QList<int> QgsAttributeList;
typedef QSet<int> QgsAttributeIds;
@ -624,11 +625,8 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
QgsVectorDataProvider *dataProvider() FINAL;
const QgsVectorDataProvider *dataProvider() const FINAL SIP_SKIP;
/**
* Returns temporal properties associated with the vector layer.
*/
QgsMapLayerTemporalProperties *temporalProperties() override;
QgsMapLayerElevationProperties *elevationProperties() override;
/**
* Sets the text \a encoding of the data provider.
@ -2867,6 +2865,8 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
//! Pointer to temporal properties
QgsVectorLayerTemporalProperties *mTemporalProperties = nullptr;
QgsVectorLayerElevationProperties *mElevationProperties = nullptr;
//! The preview expression used to generate a human readable preview string for features
QString mDisplayExpression;

View File

@ -0,0 +1,68 @@
/***************************************************************************
qgsvectorlayerelevationproperties.cpp
---------------
begin : February 2022
copyright : (C) 2022 by Nyall Dawson
email : nyall dot dawson dot com
***************************************************************************/
/***************************************************************************
* *
* 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. *
* *
***************************************************************************/
#include "qgsvectorlayerelevationproperties.h"
#include "qgsrasterlayer.h"
QgsVectorLayerElevationProperties::QgsVectorLayerElevationProperties( QObject *parent )
: QgsMapLayerElevationProperties( parent )
{
}
bool QgsVectorLayerElevationProperties::hasElevation() const
{
// layer is considered as having non-default elevation settings if we aren't clamping to terrain
return mClamping != Qgis::AltitudeClamping::Terrain;
}
QDomElement QgsVectorLayerElevationProperties::writeXml( QDomElement &parentElement, QDomDocument &document, const QgsReadWriteContext & )
{
QDomElement element = document.createElement( QStringLiteral( "elevation" ) );
element.setAttribute( QStringLiteral( "zoffset" ), qgsDoubleToString( mZOffset ) );
element.setAttribute( QStringLiteral( "zscale" ), qgsDoubleToString( mZScale ) );
element.setAttribute( QStringLiteral( "extrusion" ), qgsDoubleToString( mExtrusionHeight ) );
element.setAttribute( QStringLiteral( "clamping" ), qgsEnumValueToKey( mClamping ) );
element.setAttribute( QStringLiteral( "binding" ), qgsEnumValueToKey( mBinding ) );
parentElement.appendChild( element );
return element;
}
bool QgsVectorLayerElevationProperties::readXml( const QDomElement &element, const QgsReadWriteContext & )
{
const QDomElement elevationElement = element.firstChildElement( QStringLiteral( "elevation" ) ).toElement();
mZOffset = elevationElement.attribute( QStringLiteral( "zoffset" ), QStringLiteral( "0" ) ).toDouble();
mZScale = elevationElement.attribute( QStringLiteral( "zscale" ), QStringLiteral( "1" ) ).toDouble();
mClamping = qgsEnumKeyToValue( elevationElement.attribute( QStringLiteral( "clamping" ) ), Qgis::AltitudeClamping::Terrain );
mBinding = qgsEnumKeyToValue( elevationElement.attribute( QStringLiteral( "binding" ) ), Qgis::AltitudeBinding::Centroid );
mExtrusionHeight = elevationElement.attribute( QStringLiteral( "extrusion" ), QStringLiteral( "0" ) ).toDouble();
return true;
}
bool QgsVectorLayerElevationProperties::isVisibleInZRange( const QgsDoubleRange & ) const
{
// TODO -- test actual layer z range
return true;
}
QgsDoubleRange QgsVectorLayerElevationProperties::calculateZRange( QgsMapLayer * ) const
{
// TODO -- determine actual z range from layer statistics
return QgsDoubleRange();
}

View File

@ -0,0 +1,109 @@
/***************************************************************************
qgsvectorlayerelevationproperties.h
---------------
begin : February 2022
copyright : (C) 2022 by Nyall Dawson
email : nyall dot dawson dot com
***************************************************************************/
/***************************************************************************
* *
* 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. *
* *
***************************************************************************/
#ifndef QGSVECTORLAYERELEVATIONPROPERTIES_H
#define QGSVECTORLAYERELEVATIONPROPERTIES_H
#include "qgis_core.h"
#include "qgis_sip.h"
#include "qgis.h"
#include "qgsmaplayerelevationproperties.h"
/**
* \class QgsVectorLayerElevationProperties
* \ingroup core
* \brief Vector layer specific subclass of QgsMapLayerElevationProperties.
*
* \since QGIS 3.26
*/
class CORE_EXPORT QgsVectorLayerElevationProperties : public QgsMapLayerElevationProperties
{
Q_OBJECT
public:
/**
* Constructor for QgsVectorLayerElevationProperties, with the specified \a parent object.
*/
QgsVectorLayerElevationProperties( QObject *parent SIP_TRANSFERTHIS );
bool hasElevation() const override;
QDomElement writeXml( QDomElement &element, QDomDocument &doc, const QgsReadWriteContext &context ) override;
bool readXml( const QDomElement &element, const QgsReadWriteContext &context ) override;
bool isVisibleInZRange( const QgsDoubleRange &range ) const override;
QgsDoubleRange calculateZRange( QgsMapLayer *layer ) const override;
/**
* Returns the altitude clamping method, which dictates how feature heights are interpreted
* with respect to terrain heights.
*
* \see setClamping()
*/
Qgis::AltitudeClamping clamping() const { return mClamping; }
/**
* Sets the altitude \a clamping method, which dictates how feature heights are interpreted
* with respect to terrain heights.
*
* \see clamping()
*/
void setClamping( Qgis::AltitudeClamping clamping ) { mClamping = clamping; }
/**
* Returns the altitude binding method, which determines how altitude is bound to individual vertices in features.
*
* \see setBinding()
*/
Qgis::AltitudeBinding binding() const { return mBinding; }
/**
* Sets the altitude \a binding method, which determines how altitude is bound to individual vertices in features.
*
* \see binding()
*/
void setBinding( Qgis::AltitudeBinding binding ) { mBinding = binding; }
/**
* Returns the feature extrusion height.
*
* \note the zScale() factor is NOT applied to extrusion heights.
*
* \see setExtrusionHeight()
*/
double extrusionHeight() const { return mExtrusionHeight; }
/**
* Sets the feature extrusion height.
*
* \note the zScale() factor is NOT applied to extrusion heights.
*
* \see extrusionHeight()
*/
void setExtrusionHeight( double height ) { mExtrusionHeight = height; }
private:
Qgis::AltitudeClamping mClamping = Qgis::AltitudeClamping::Terrain;
Qgis::AltitudeBinding mBinding = Qgis::AltitudeBinding::Centroid;
double mExtrusionHeight = 0;
};
#endif // QGSVECTORLAYERELEVATIONPROPERTIES_H

View File

@ -0,0 +1,228 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QgsVectorElevationPropertiesWidgetBase</class>
<widget class="QWidget" name="QgsVectorElevationPropertiesWidgetBase">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>555</width>
<height>445</height>
</rect>
</property>
<property name="windowTitle">
<string>Vector Elevation Properties</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="mElevationGroupBox">
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="title">
<string>Elevation Clamping</string>
</property>
<property name="checkable">
<bool>false</bool>
</property>
<property name="syncGroup" stdset="0">
<string notr="true">vectorgeneral</string>
</property>
<layout class="QGridLayout" name="gridLayout_2" columnstretch="0,1,0">
<item row="3" column="1" colspan="2">
<widget class="QgsDoubleSpinBox" name="mOffsetZSpinBox">
<property name="decimals">
<number>6</number>
</property>
<property name="minimum">
<double>-99999999999.000000000000000</double>
</property>
<property name="maximum">
<double>99999999999.000000000000000</double>
</property>
</widget>
</item>
<item row="1" column="0" colspan="3">
<widget class="QLabel" name="mLabelClampingExplanation">
<property name="text">
<string/>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0" colspan="3">
<widget class="QComboBox" name="mComboClamping"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Offset</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="mLabelScale">
<property name="text">
<string>Scale</string>
</property>
</widget>
</item>
<item row="2" column="1" colspan="2">
<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>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="mExtrusionGroupBox">
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="title">
<string>Extrusion</string>
</property>
<property name="checkable">
<bool>false</bool>
</property>
<property name="syncGroup" stdset="0">
<string notr="true">vectorgeneral</string>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<item row="3" column="0">
<widget class="Line" name="line_5">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="mLabelBindingExplanation_2">
<property name="text">
<string>Extrusion controls how high features extend vertically above their base.</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Height</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QgsDoubleSpinBox" name="mExtrusionSpinBox">
<property name="decimals">
<number>6</number>
</property>
<property name="minimum">
<double>-99999999999.000000000000000</double>
</property>
<property name="maximum">
<double>99999999999.000000000000000</double>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="mBindingGroupBox">
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="title">
<string>Elevation Binding</string>
</property>
<property name="checkable">
<bool>false</bool>
</property>
<property name="syncGroup" stdset="0">
<string notr="true">vectorgeneral</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0" colspan="2">
<widget class="QComboBox" name="mComboBinding"/>
</item>
<item row="2" column="0">
<widget class="Line" name="line_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QLabel" name="mLabelBindingExplanation">
<property name="text">
<string/>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QgsDoubleSpinBox</class>
<extends>QDoubleSpinBox</extends>
<header>qgsdoublespinbox.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>mElevationGroupBox</tabstop>
<tabstop>mScaleZSpinBox</tabstop>
<tabstop>mOffsetZSpinBox</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View File

@ -349,6 +349,7 @@ ADD_PYTHON_TEST(PyQgsVectorFieldMarkerSymbolLayer test_qgsvectorfieldmarkersymbo
ADD_PYTHON_TEST(PyQgsVectorFileWriter test_qgsvectorfilewriter.py)
ADD_PYTHON_TEST(PyQgsVectorFileWriterTask test_qgsvectorfilewritertask.py)
ADD_PYTHON_TEST(PyQgsVectorLayer test_qgsvectorlayer.py)
ADD_PYTHON_TEST(PyQgsVectorLayerElevationProperties test_qgsvectorlayerelevationproperties.py)
ADD_PYTHON_TEST(PyQgsVectorLayerFeatureCounter test_qgsvectorlayerfeaturecounter.py)
ADD_PYTHON_TEST(PyQgsVectorLayerCache test_qgsvectorlayercache.py)
ADD_PYTHON_TEST(PyQgsVectorLayerEditBuffer test_qgsvectorlayereditbuffer.py)

View File

@ -49,7 +49,7 @@ class TestQgsRasterLayerElevationProperties(unittest.TestCase):
props2.readXml(elem, QgsReadWriteContext())
self.assertEqual(props2.zScale(), 2)
self.assertEqual(props2.zOffset(), 0.5)
self.assertTrue(props.isEnabled())
self.assertTrue(props2.isEnabled())
if __name__ == '__main__':

View File

@ -0,0 +1,65 @@
# -*- coding: utf-8 -*-
"""QGIS Unit tests for QgsVectorLayerElevationProperties
.. 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 (
Qgis,
QgsVectorLayerElevationProperties,
QgsReadWriteContext,
)
from qgis.PyQt.QtXml import QDomDocument
from qgis.testing import start_app, unittest
start_app()
class TestQgsVectorLayerElevationProperties(unittest.TestCase):
def testBasic(self):
props = QgsVectorLayerElevationProperties(None)
self.assertEqual(props.zScale(), 1)
self.assertEqual(props.zOffset(), 0)
self.assertEqual(props.extrusionHeight(), 0)
self.assertFalse(props.hasElevation())
self.assertEqual(props.clamping(), Qgis.AltitudeClamping.Terrain)
self.assertEqual(props.binding(), Qgis.AltitudeBinding.Centroid)
props.setZOffset(0.5)
props.setZScale(2)
props.setClamping(Qgis.AltitudeClamping.Relative)
props.setBinding(Qgis.AltitudeBinding.Vertex)
props.setExtrusionHeight(10)
self.assertEqual(props.zScale(), 2)
self.assertEqual(props.zOffset(), 0.5)
self.assertEqual(props.extrusionHeight(), 10)
self.assertTrue(props.hasElevation())
self.assertEqual(props.clamping(), Qgis.AltitudeClamping.Relative)
self.assertEqual(props.binding(), Qgis.AltitudeBinding.Vertex)
doc = QDomDocument("testdoc")
elem = doc.createElement('test')
props.writeXml(elem, doc, QgsReadWriteContext())
props2 = QgsVectorLayerElevationProperties(None)
props2.readXml(elem, QgsReadWriteContext())
self.assertEqual(props2.zScale(), 2)
self.assertEqual(props2.zOffset(), 0.5)
self.assertEqual(props2.clamping(), Qgis.AltitudeClamping.Relative)
self.assertEqual(props2.binding(), Qgis.AltitudeBinding.Vertex)
self.assertEqual(props2.extrusionHeight(), 10)
if __name__ == '__main__':
unittest.main()