Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Add option to limit the extent of "fill above" and "fill below"
elevation ranges for a layer in elevation profile plots

Fixes #51010
  • Loading branch information
nyalldawson committed May 22, 2023
1 parent 6c4f96a commit 4c6018d
Show file tree
Hide file tree
Showing 26 changed files with 595 additions and 66 deletions.
Expand Up @@ -91,6 +91,32 @@ Returns the symbology option used to render the mesh profile in elevation profil
Sets the ``symbology`` option used to render the mesh profile in elevation profile plots.

.. seealso:: :py:func:`setProfileSymbology`
%End

double elevationLimit() const;
%Docstring
Returns the elevation limit, which is used when :py:func:`~QgsMeshLayerElevationProperties.profileSymbology` is
:py:class:`Qgis`.ProfileSurfaceSymbology.FillBelow or :py:class:`Qgis`.ProfileSurfaceSymbology.FillAbove
to limit the fill to a specific elevation range.

By default this is NaN, which indicates that there is no elevation limit.

.. seealso:: :py:func:`setElevationLimit`

.. versionadded:: 3.32
%End

void setElevationLimit( double limit );
%Docstring
Sets the elevation ``limit``, which is used when :py:func:`~QgsMeshLayerElevationProperties.profileSymbology` is
:py:class:`Qgis`.ProfileSurfaceSymbology.FillBelow or :py:class:`Qgis`.ProfileSurfaceSymbology.FillAbove
to limit the fill to a specific elevation range.

Set to NaN to indicate that there is no elevation limit.

.. seealso:: :py:func:`elevationLimit`

.. versionadded:: 3.32
%End

};
Expand Down
Expand Up @@ -119,6 +119,32 @@ Returns the symbology option used to render the raster profile in elevation prof
Sets the ``symbology`` option used to render the raster profile in elevation profile plots.

.. seealso:: :py:func:`setProfileSymbology`
%End

double elevationLimit() const;
%Docstring
Returns the elevation limit, which is used when :py:func:`~QgsRasterLayerElevationProperties.profileSymbology` is
:py:class:`Qgis`.ProfileSurfaceSymbology.FillBelow or :py:class:`Qgis`.ProfileSurfaceSymbology.FillAbove
to limit the fill to a specific elevation range.

By default this is NaN, which indicates that there is no elevation limit.

.. seealso:: :py:func:`setElevationLimit`

.. versionadded:: 3.32
%End

void setElevationLimit( double limit );
%Docstring
Sets the elevation ``limit``, which is used when :py:func:`~QgsRasterLayerElevationProperties.profileSymbology` is
:py:class:`Qgis`.ProfileSurfaceSymbology.FillBelow or :py:class:`Qgis`.ProfileSurfaceSymbology.FillAbove
to limit the fill to a specific elevation range.

Set to NaN to indicate that there is no elevation limit.

.. seealso:: :py:func:`elevationLimit`

.. versionadded:: 3.32
%End

static bool layerLooksLikeDem( QgsRasterLayer *layer );
Expand Down
Expand Up @@ -267,6 +267,32 @@ Sets the ``symbology`` option used to render the vector profile in elevation pro
This setting is only used when :py:func:`~QgsVectorLayerElevationProperties.type` is :py:class:`Qgis`.VectorProfileType.ContinuousSurface.

.. seealso:: :py:func:`setProfileSymbology`
%End

double elevationLimit() const;
%Docstring
Returns the elevation limit, which is used when :py:func:`~QgsVectorLayerElevationProperties.profileSymbology` is
:py:class:`Qgis`.ProfileSurfaceSymbology.FillBelow or :py:class:`Qgis`.ProfileSurfaceSymbology.FillAbove
to limit the fill to a specific elevation range.

By default this is NaN, which indicates that there is no elevation limit.

.. seealso:: :py:func:`setElevationLimit`

.. versionadded:: 3.32
%End

void setElevationLimit( double limit );
%Docstring
Sets the elevation ``limit``, which is used when :py:func:`~QgsVectorLayerElevationProperties.profileSymbology` is
:py:class:`Qgis`.ProfileSurfaceSymbology.FillBelow or :py:class:`Qgis`.ProfileSurfaceSymbology.FillAbove
to limit the fill to a specific elevation range.

Set to NaN to indicate that there is no elevation limit.

.. seealso:: :py:func:`elevationLimit`

.. versionadded:: 3.32
%End

bool showMarkerSymbolInSurfacePlots() const;
Expand Down
10 changes: 10 additions & 0 deletions src/app/mesh/qgsmeshelevationpropertieswidget.cpp
Expand Up @@ -34,11 +34,13 @@ QgsMeshElevationPropertiesWidget::QgsMeshElevationPropertiesWidget( QgsMeshLayer
mStyleComboBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "mIconSurfaceElevationLine.svg" ) ), tr( "Line" ), static_cast< int >( Qgis::ProfileSurfaceSymbology::Line ) );
mStyleComboBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "mIconSurfaceElevationFillBelow.svg" ) ), tr( "Fill Below" ), static_cast< int >( Qgis::ProfileSurfaceSymbology::FillBelow ) );
mStyleComboBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "mIconSurfaceElevationFillAbove.svg" ) ), tr( "Fill Above" ), static_cast< int >( Qgis::ProfileSurfaceSymbology::FillAbove ) );
mElevationLimitSpinBox->setClearValue( mElevationLimitSpinBox->minimum(), tr( "No set" ) );

syncToLayer( layer );

connect( mOffsetZSpinBox, qOverload<double >( &QDoubleSpinBox::valueChanged ), this, &QgsMeshElevationPropertiesWidget::onChanged );
connect( mScaleZSpinBox, qOverload<double >( &QDoubleSpinBox::valueChanged ), this, &QgsMeshElevationPropertiesWidget::onChanged );
connect( mElevationLimitSpinBox, qOverload<double >( &QDoubleSpinBox::valueChanged ), this, &QgsMeshElevationPropertiesWidget::onChanged );
connect( mLineStyleButton, &QgsSymbolButton::changed, this, &QgsMeshElevationPropertiesWidget::onChanged );
connect( mFillStyleButton, &QgsSymbolButton::changed, this, &QgsMeshElevationPropertiesWidget::onChanged );
connect( mStyleComboBox, qOverload< int >( &QComboBox::currentIndexChanged ), this, [ = ]
Expand Down Expand Up @@ -70,6 +72,10 @@ void QgsMeshElevationPropertiesWidget::syncToLayer( QgsMapLayer *layer )
const QgsMeshLayerElevationProperties *props = qgis::down_cast< const QgsMeshLayerElevationProperties * >( mLayer->elevationProperties() );
mOffsetZSpinBox->setValue( props->zOffset() );
mScaleZSpinBox->setValue( props->zScale() );
if ( std::isnan( props->elevationLimit() ) )
mElevationLimitSpinBox->clear();
else
mElevationLimitSpinBox->setValue( props->elevationLimit() );
mLineStyleButton->setSymbol( props->profileLineSymbol()->clone() );
mFillStyleButton->setSymbol( props->profileFillSymbol()->clone() );

Expand All @@ -96,6 +102,10 @@ void QgsMeshElevationPropertiesWidget::apply()
QgsMeshLayerElevationProperties *props = qgis::down_cast< QgsMeshLayerElevationProperties * >( mLayer->elevationProperties() );
props->setZOffset( mOffsetZSpinBox->value() );
props->setZScale( mScaleZSpinBox->value() );
if ( mElevationLimitSpinBox->value() != mElevationLimitSpinBox->clearValue() )
props->setElevationLimit( mElevationLimitSpinBox->value() );
else
props->setElevationLimit( std::numeric_limits< double >::quiet_NaN() );
props->setProfileLineSymbol( mLineStyleButton->clonedSymbol< QgsLineSymbol >() );
props->setProfileFillSymbol( mFillStyleButton->clonedSymbol< QgsFillSymbol >() );
props->setProfileSymbology( static_cast< Qgis::ProfileSurfaceSymbology >( mStyleComboBox->currentData().toInt() ) );
Expand Down
10 changes: 10 additions & 0 deletions src/app/raster/qgsrasterelevationpropertieswidget.cpp
Expand Up @@ -35,11 +35,13 @@ QgsRasterElevationPropertiesWidget::QgsRasterElevationPropertiesWidget( QgsRaste
mStyleComboBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "mIconSurfaceElevationLine.svg" ) ), tr( "Line" ), static_cast< int >( Qgis::ProfileSurfaceSymbology::Line ) );
mStyleComboBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "mIconSurfaceElevationFillBelow.svg" ) ), tr( "Fill Below" ), static_cast< int >( Qgis::ProfileSurfaceSymbology::FillBelow ) );
mStyleComboBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "mIconSurfaceElevationFillAbove.svg" ) ), tr( "Fill Above" ), static_cast< int >( Qgis::ProfileSurfaceSymbology::FillAbove ) );
mElevationLimitSpinBox->setClearValue( mElevationLimitSpinBox->minimum(), tr( "No set" ) );

syncToLayer( layer );

connect( mOffsetZSpinBox, qOverload<double >( &QDoubleSpinBox::valueChanged ), this, &QgsRasterElevationPropertiesWidget::onChanged );
connect( mScaleZSpinBox, qOverload<double >( &QDoubleSpinBox::valueChanged ), this, &QgsRasterElevationPropertiesWidget::onChanged );
connect( mElevationLimitSpinBox, qOverload<double >( &QDoubleSpinBox::valueChanged ), this, &QgsRasterElevationPropertiesWidget::onChanged );
connect( mElevationGroupBox, &QGroupBox::toggled, this, &QgsRasterElevationPropertiesWidget::onChanged );
connect( mLineStyleButton, &QgsSymbolButton::changed, this, &QgsRasterElevationPropertiesWidget::onChanged );
connect( mFillStyleButton, &QgsSymbolButton::changed, this, &QgsRasterElevationPropertiesWidget::onChanged );
Expand Down Expand Up @@ -74,6 +76,10 @@ void QgsRasterElevationPropertiesWidget::syncToLayer( QgsMapLayer *layer )
mElevationGroupBox->setChecked( props->isEnabled() );
mOffsetZSpinBox->setValue( props->zOffset() );
mScaleZSpinBox->setValue( props->zScale() );
if ( std::isnan( props->elevationLimit() ) )
mElevationLimitSpinBox->clear();
else
mElevationLimitSpinBox->setValue( props->elevationLimit() );
mLineStyleButton->setSymbol( props->profileLineSymbol()->clone() );
mFillStyleButton->setSymbol( props->profileFillSymbol()->clone() );
mBandComboBox->setLayer( mLayer );
Expand Down Expand Up @@ -102,6 +108,10 @@ void QgsRasterElevationPropertiesWidget::apply()
props->setEnabled( mElevationGroupBox->isChecked() );
props->setZOffset( mOffsetZSpinBox->value() );
props->setZScale( mScaleZSpinBox->value() );
if ( mElevationLimitSpinBox->value() != mElevationLimitSpinBox->clearValue() )
props->setElevationLimit( mElevationLimitSpinBox->value() );
else
props->setElevationLimit( std::numeric_limits< double >::quiet_NaN() );
props->setProfileLineSymbol( mLineStyleButton->clonedSymbol< QgsLineSymbol >() );
props->setProfileFillSymbol( mFillStyleButton->clonedSymbol< QgsFillSymbol >() );
props->setProfileSymbology( static_cast< Qgis::ProfileSurfaceSymbology >( mStyleComboBox->currentData().toInt() ) );
Expand Down
10 changes: 10 additions & 0 deletions src/app/vector/qgsvectorelevationpropertieswidget.cpp
Expand Up @@ -39,6 +39,7 @@ QgsVectorElevationPropertiesWidget::QgsVectorElevationPropertiesWidget( QgsVecto
mSurfaceLineStyleButton->setSymbolType( Qgis::SymbolType::Line );
mSurfaceFillStyleButton->setSymbolType( Qgis::SymbolType::Fill );
mSurfaceMarkerStyleButton->setSymbolType( Qgis::SymbolType::Marker );
mElevationLimitSpinBox->setClearValue( mElevationLimitSpinBox->minimum(), tr( "No set" ) );

mComboClamping->addItem( tr( "Clamped to Terrain" ), static_cast< int >( Qgis::AltitudeClamping::Terrain ) );
mComboClamping->addItem( tr( "Relative to Terrain" ), static_cast< int >( Qgis::AltitudeClamping::Relative ) );
Expand All @@ -61,6 +62,7 @@ QgsVectorElevationPropertiesWidget::QgsVectorElevationPropertiesWidget( QgsVecto

connect( mOffsetZSpinBox, qOverload<double >( &QDoubleSpinBox::valueChanged ), this, &QgsVectorElevationPropertiesWidget::onChanged );
connect( mScaleZSpinBox, qOverload<double >( &QDoubleSpinBox::valueChanged ), this, &QgsVectorElevationPropertiesWidget::onChanged );
connect( mElevationLimitSpinBox, qOverload<double >( &QDoubleSpinBox::valueChanged ), this, &QgsVectorElevationPropertiesWidget::onChanged );
connect( mExtrusionSpinBox, qOverload<double >( &QDoubleSpinBox::valueChanged ), this, &QgsVectorElevationPropertiesWidget::onChanged );
connect( mExtrusionGroupBox, &QGroupBox::toggled, this, &QgsVectorElevationPropertiesWidget::onChanged );
connect( mComboClamping, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsVectorElevationPropertiesWidget::onChanged );
Expand Down Expand Up @@ -133,6 +135,10 @@ void QgsVectorElevationPropertiesWidget::syncToLayer( QgsMapLayer *layer )
mComboBinding->setCurrentIndex( mComboBinding->findData( static_cast< int >( props->binding() ) ) );
mOffsetZSpinBox->setValue( props->zOffset() );
mScaleZSpinBox->setValue( props->zScale() );
if ( std::isnan( props->elevationLimit() ) )
mElevationLimitSpinBox->clear();
else
mElevationLimitSpinBox->setValue( props->elevationLimit() );
mExtrusionGroupBox->setChecked( props->extrusionEnabled() );
mExtrusionSpinBox->setValue( props->extrusionHeight() );
mTypeComboBox->setCurrentIndex( mTypeComboBox->findData( static_cast< int >( props->type() ) ) );
Expand Down Expand Up @@ -212,6 +218,10 @@ void QgsVectorElevationPropertiesWidget::apply()
props->setBinding( static_cast< Qgis::AltitudeBinding >( mComboBinding->currentData().toInt() ) );
props->setExtrusionEnabled( mExtrusionGroupBox->isChecked() );
props->setExtrusionHeight( mExtrusionSpinBox->value() );
if ( mElevationLimitSpinBox->value() != mElevationLimitSpinBox->clearValue() )
props->setElevationLimit( mElevationLimitSpinBox->value() );
else
props->setElevationLimit( std::numeric_limits< double >::quiet_NaN() );

props->setRespectLayerSymbology( mCheckRespectLayerSymbology->isChecked() );
props->setShowMarkerSymbolInSurfacePlots( mCheckBoxShowMarkersAtSampledPoints->isChecked() );
Expand Down
43 changes: 41 additions & 2 deletions src/core/elevation/qgsabstractprofilesurfacegenerator.cpp
Expand Up @@ -134,8 +134,8 @@ void QgsAbstractProfileSurfaceResults::renderResults( QgsProfileRenderContext &c

const double minDistance = context.distanceRange().lower();
const double maxDistance = context.distanceRange().upper();
const double minZ = context.elevationRange().lower();
const double maxZ = context.elevationRange().upper();
double minZ = context.elevationRange().lower();
double maxZ = context.elevationRange().upper();

const QRectF visibleRegion( minDistance, minZ, maxDistance - minDistance, maxZ - minZ );
QPainterPath clipPath;
Expand All @@ -148,8 +148,36 @@ void QgsAbstractProfileSurfaceResults::renderResults( QgsProfileRenderContext &c
mLineSymbol->startRender( context.renderContext() );
break;
case Qgis::ProfileSurfaceSymbology::FillBelow:
mFillSymbol->startRender( context.renderContext() );
if ( !std::isnan( mElevationLimit ) )
{
double dataLimit = std::numeric_limits< double >::max();
for ( auto pointIt = mDistanceToHeightMap.constBegin(); pointIt != mDistanceToHeightMap.constEnd(); ++pointIt )
{
if ( !std::isnan( pointIt.value() ) )
{
dataLimit = std::min( pointIt.value(), dataLimit );
}
}
if ( dataLimit > mElevationLimit )
minZ = std::max( minZ, mElevationLimit );
}
break;
case Qgis::ProfileSurfaceSymbology::FillAbove:
mFillSymbol->startRender( context.renderContext() );
if ( !std::isnan( mElevationLimit ) )
{
double dataLimit = std::numeric_limits< double >::lowest();
for ( auto pointIt = mDistanceToHeightMap.constBegin(); pointIt != mDistanceToHeightMap.constEnd(); ++pointIt )
{
if ( !std::isnan( pointIt.value() ) )
{
dataLimit = std::max( pointIt.value(), dataLimit );
}
}
if ( dataLimit < mElevationLimit )
maxZ = std::min( maxZ, mElevationLimit );
}
break;
}

Expand Down Expand Up @@ -235,6 +263,7 @@ void QgsAbstractProfileSurfaceResults::copyPropertiesFromGenerator( const QgsAbs
mLineSymbol.reset( surfaceGenerator->lineSymbol()->clone() );
mFillSymbol.reset( surfaceGenerator->fillSymbol()->clone() );
symbology = surfaceGenerator->symbology();
mElevationLimit = surfaceGenerator->elevationLimit();
}

//
Expand All @@ -257,3 +286,13 @@ QgsFillSymbol *QgsAbstractProfileSurfaceGenerator::fillSymbol() const
{
return mFillSymbol.get();
}

double QgsAbstractProfileSurfaceGenerator::elevationLimit() const
{
return mElevationLimit;
}

void QgsAbstractProfileSurfaceGenerator::setElevationLimit( double limit )
{
mElevationLimit = limit;
}
26 changes: 26 additions & 0 deletions src/core/elevation/qgsabstractprofilesurfacegenerator.h
Expand Up @@ -48,6 +48,7 @@ class CORE_EXPORT QgsAbstractProfileSurfaceResults : public QgsAbstractProfileRe
Qgis::ProfileSurfaceSymbology symbology = Qgis::ProfileSurfaceSymbology::Line;
std::unique_ptr< QgsLineSymbol > mLineSymbol;
std::unique_ptr< QgsFillSymbol > mFillSymbol;
double mElevationLimit = std::numeric_limits< double >::quiet_NaN();

QMap< double, double > distanceToHeightMap() const override;
QgsPointSequence sampledPoints() const override;
Expand Down Expand Up @@ -87,11 +88,36 @@ class CORE_EXPORT QgsAbstractProfileSurfaceGenerator : public QgsAbstractProfile
*/
QgsFillSymbol *fillSymbol() const;

/**
* Returns the elevation limit, which is used when symbology() is
* Qgis::ProfileSurfaceSymbology::FillBelow or Qgis::ProfileSurfaceSymbology::FillAbove
* to limit the fill to a specific elevation range.
*
* By default this is NaN, which indicates that there is no elevation limit.
*
* \see setElevationLimit()
* \since QGIS 3.32
*/
double elevationLimit() const;

/**
* Sets the elevation \a limit, which is used when symbology() is
* Qgis::ProfileSurfaceSymbology::FillBelow or Qgis::ProfileSurfaceSymbology::FillAbove
* to limit the fill to a specific elevation range.
*
* Set to NaN to indicate that there is no elevation limit.
*
* \see elevationLimit()
* \since QGIS 3.32
*/
void setElevationLimit( double limit );

protected:

Qgis::ProfileSurfaceSymbology mSymbology = Qgis::ProfileSurfaceSymbology::Line;
std::unique_ptr< QgsLineSymbol > mLineSymbol;
std::unique_ptr< QgsFillSymbol > mFillSymbol;
double mElevationLimit = std::numeric_limits< double >::quiet_NaN();

};

Expand Down
30 changes: 30 additions & 0 deletions src/core/mesh/qgsmeshlayerelevationproperties.cpp
Expand Up @@ -44,6 +44,9 @@ QDomElement QgsMeshLayerElevationProperties::writeXml( QDomElement &parentElemen
{
QDomElement element = document.createElement( QStringLiteral( "elevation" ) );
element.setAttribute( QStringLiteral( "symbology" ), qgsEnumValueToKey( mSymbology ) );
if ( !std::isnan( mElevationLimit ) )
element.setAttribute( QStringLiteral( "elevationLimit" ), qgsDoubleToString( mElevationLimit ) );

writeCommonProperties( element, document, context );

QDomElement profileLineSymbolElement = document.createElement( QStringLiteral( "profileLineSymbol" ) );
Expand All @@ -62,6 +65,10 @@ bool QgsMeshLayerElevationProperties::readXml( const QDomElement &element, const
{
const QDomElement elevationElement = element.firstChildElement( QStringLiteral( "elevation" ) ).toElement();
mSymbology = qgsEnumKeyToValue( elevationElement.attribute( QStringLiteral( "symbology" ) ), Qgis::ProfileSurfaceSymbology::Line );
if ( elevationElement.hasAttribute( QStringLiteral( "elevationLimit" ) ) )
mElevationLimit = elevationElement.attribute( QStringLiteral( "elevationLimit" ) ).toDouble();
else
mElevationLimit = std::numeric_limits< double >::quiet_NaN();

readCommonProperties( elevationElement, context );

Expand Down Expand Up @@ -94,6 +101,7 @@ QgsMeshLayerElevationProperties *QgsMeshLayerElevationProperties::clone() const
res->setProfileLineSymbol( mProfileLineSymbol->clone() );
res->setProfileFillSymbol( mProfileFillSymbol->clone() );
res->setProfileSymbology( mSymbology );
res->setElevationLimit( mElevationLimit );
res->copyCommonProperties( this );
return res.release();
}
Expand Down Expand Up @@ -135,11 +143,33 @@ QgsFillSymbol *QgsMeshLayerElevationProperties::profileFillSymbol() const
void QgsMeshLayerElevationProperties::setProfileFillSymbol( QgsFillSymbol *symbol )
{
mProfileFillSymbol.reset( symbol );
emit changed();
emit profileRenderingPropertyChanged();
}

void QgsMeshLayerElevationProperties::setProfileSymbology( Qgis::ProfileSurfaceSymbology symbology )
{
if ( mSymbology == symbology )
return;

mSymbology = symbology;
emit changed();
emit profileRenderingPropertyChanged();
}

double QgsMeshLayerElevationProperties::elevationLimit() const
{
return mElevationLimit;
}

void QgsMeshLayerElevationProperties::setElevationLimit( double limit )
{
if ( qgsDoubleNear( mElevationLimit, limit ) )
return;

mElevationLimit = limit;
emit changed();
emit profileRenderingPropertyChanged();
}

void QgsMeshLayerElevationProperties::setDefaultProfileLineSymbol( const QColor &color )
Expand Down

0 comments on commit 4c6018d

Please sign in to comment.