Refactor elevation map related code to a new class QgsElevationMap

This commit is contained in:
Martin Dobias 2022-08-11 22:37:53 +02:00
parent 8f933496b6
commit 18b3bdd139
10 changed files with 202 additions and 90 deletions

View File

@ -48,29 +48,6 @@ Returns a reference to the context's render context.
%End
QPainter *elevationPainter() const;
%Docstring
Returns the painter used to render elevation data
.. versionadded:: 3.28
%End
void setUseElevationMap( bool useEyeDomeLighting );
%Docstring
Sets whether eye dome lighting will be used
.. versionadded:: 3.28
%End
bool useElevationMap() const;
%Docstring
Returns whether eye dome lighting will be used
.. versionadded:: 3.28
%End
QgsVector3D scale() const;
%Docstring
Returns the scale of the layer's int32 coordinates compared to CRS coords.
@ -179,6 +156,24 @@ Returns the feedback object used to cancel rendering
.. versionadded:: 3.20
%End
void setElevationMap( QgsElevationMap *elevationMap /Transfer/ );
%Docstring
Sets elevation map that will be used to record elevation of rendered points.
.. note::
Takes ownership of the passed object
.. versionadded:: 3.28
%End
QgsElevationMap *elevationMap();
%Docstring
Returns elevation map. It may be a null pointer if elevation map is not needed in rendering.
.. versionadded:: 3.28
%End
private:
QgsPointCloudRenderContext( const QgsPointCloudRenderContext &rh );

View File

@ -368,6 +368,7 @@ set(QGIS_CORE_SRCS
qgsdefaultvalue.cpp
qgsdiagramrenderer.cpp
qgsdistancearea.cpp
qgselevationmap.cpp
qgselevationutils.cpp
qgserror.cpp
qgseventtracing.cpp
@ -1032,6 +1033,7 @@ set(QGIS_CORE_HDRS
qgsdiagramrenderer.h
qgsdistancearea.h
qgseditorwidgetsetup.h
qgselevationmap.h
qgselevationutils.h
qgserror.h
qgseventtracing.h

View File

@ -62,7 +62,7 @@ void QgsPointCloudAttributeByRampRenderer::renderBlock( const QgsPointCloudBlock
return;
const QgsPointCloudAttribute::DataType attributeType = attribute->type();
const bool renderElevation = context.useElevationMap();
const bool renderElevation = context.elevationMap();
const QgsDoubleRange zRange = context.renderContext().zRange();
const bool considerZ = !zRange.isInfinite() || renderElevation;

View File

@ -80,7 +80,7 @@ void QgsPointCloudClassifiedRenderer::renderBlock( const QgsPointCloudBlock *blo
return;
const QgsPointCloudAttribute::DataType attributeType = attribute->type();
const bool renderElevation = context.useElevationMap();
const bool renderElevation = context.elevationMap();
const QgsDoubleRange zRange = context.renderContext().zRange();
const bool considerZ = !zRange.isInfinite() || renderElevation;

View File

@ -24,6 +24,7 @@
#include "qgspointcloudindex.h"
#include "qgsstyle.h"
#include "qgscolorramp.h"
#include "qgselevationmap.h"
#include "qgspointcloudrequest.h"
#include "qgspointcloudattribute.h"
#include "qgspointcloudrenderer.h"
@ -115,20 +116,12 @@ bool QgsPointCloudLayerRenderer::render()
// Set up the render configuration options
QPainter *painter = context.renderContext().painter();
std::unique_ptr<QImage> elevationImage;
std::unique_ptr<QPainter> elevationPainter;
bool applyEdl = mRenderer && mRenderer->useEyeDomeLighting();
context.setUseElevationMap( applyEdl );
if ( QImage *painterImage = dynamic_cast<QImage *>( painter->device() ) )
{
if ( applyEdl )
{
elevationImage.reset( new QImage( painterImage->size(), QImage::Format_ARGB32 ) );
elevationImage->fill( QColor::fromRgbF( 0, 0, 0, 0 ) );
elevationPainter.reset( new QPainter );
elevationPainter->begin( elevationImage.get() );
context.setElevationPainter( elevationPainter.get() );
}
context.setElevationMap( new QgsElevationMap( painterImage->size() ) );
}
QgsScopedQPainterState painterState( painter );
@ -289,16 +282,10 @@ bool QgsPointCloudLayerRenderer::render()
{
if ( QImage *drawnImage = dynamic_cast<QImage *>( painter->device() ) )
{
if ( QPainter *elevationPainter = context.elevationPainter() )
{
elevationPainter->end();
}
double strength = mRenderer->eyeDomeLightingStrength();
int distance = mRenderer->eyeDomeLightingDistance();
float zScale = context.renderContext().rendererScale() / 3 / 10000;
applyEyeDomeLighting( drawnImage, elevationImage.get(), distance, strength, zScale );
float zScale = context.renderContext().rendererScale() / 3;
context.elevationMap()->applyEyeDomeLighting( *drawnImage, distance, strength, zScale );
}
}

View File

@ -18,6 +18,7 @@
#include "qgspointcloudrenderer.h"
#include "qgspointcloudrendererregistry.h"
#include "qgsapplication.h"
#include "qgselevationmap.h"
#include "qgssymbollayerutils.h"
#include "qgspointcloudlayer.h"
#include "qgspointcloudindex.h"
@ -38,6 +39,11 @@ QgsPointCloudRenderContext::QgsPointCloudRenderContext( QgsRenderContext &contex
}
void QgsPointCloudRenderContext::setElevationMap( QgsElevationMap *elevationMap )
{
mElevationMap.reset( elevationMap );
}
long QgsPointCloudRenderContext::pointsRendered() const
{
return mPointsRendered;
@ -159,30 +165,13 @@ QStringList QgsPointCloudRenderer::legendRuleKeys() const
return QStringList();
}
QColor encodeElevation( float zFloat )
{
int zInt = zFloat * 0x00ffffff;
QColor c;
c.setRed( ( zInt & 0xff0000 ) >> 16 );
c.setGreen( ( zInt & 0xff00 ) >> 8 );
c.setBlue( zInt & 0xff );
c.setAlphaF( 1.0f );
return c;
}
void QgsPointCloudRenderer::drawPointToElevationMap( double x, double y, double z, QgsPointCloudRenderContext &context ) const
{
const QPointF originalXY( x, y );
context.renderContext().mapToPixel().transformInPlace( x, y );
QPainter *elevationPainter = context.elevationPainter();
QPainter *elevationPainter = context.elevationMap()->painter();
const double zMin = -5000;
const double zMax = +5000;
float zFloat = ( z - zMin ) / ( zMax - zMin );
zFloat = std::clamp<float>( zFloat, 0.0, 1.0 );
QBrush brush( encodeElevation( zFloat ) );
QBrush brush( QgsElevationMap::encodeElevation( z ) );
switch ( mPointSymbol )
{
case Qgis::PointCloudSymbol::Square:

View File

@ -29,6 +29,7 @@ class QgsPointCloudBlock;
class QgsLayerTreeLayer;
class QgsLayerTreeModelLegendNode;
class QgsPointCloudLayer;
class QgsElevationMap;
/**
* \ingroup core
@ -74,31 +75,6 @@ class CORE_EXPORT QgsPointCloudRenderContext
*/
const QgsRenderContext &renderContext() const { return mRenderContext; } SIP_SKIP
/**
* Sets the painter used to render elevation data
* \since QGIS 3.28
*/
void setElevationPainter( QPainter *painter ) SIP_SKIP { mElevationPainter = painter; }
/**
* Returns the painter used to render elevation data
* \since QGIS 3.28
*/
QPainter *elevationPainter() const { return mElevationPainter; }
/**
* Sets whether eye dome lighting will be used
* \since QGIS 3.28
*/
void setUseElevationMap( bool useEyeDomeLighting ) { mUseElevationMap = useEyeDomeLighting; }
/**
* Returns whether eye dome lighting will be used
* \since QGIS 3.28
*/
bool useElevationMap() const { return mUseElevationMap; }
/**
* Returns the scale of the layer's int32 coordinates compared to CRS coords.
*/
@ -198,6 +174,21 @@ class CORE_EXPORT QgsPointCloudRenderContext
*/
QgsFeedback *feedback() const { return mFeedback; }
/**
* Sets elevation map that will be used to record elevation of rendered points.
* \note Takes ownership of the passed object
*
* \since QGIS 3.28
*/
void setElevationMap( QgsElevationMap *elevationMap SIP_TRANSFER );
/**
* Returns elevation map. It may be a null pointer if elevation map is not needed in rendering.
*
* \since QGIS 3.28
*/
QgsElevationMap *elevationMap() { return mElevationMap.get(); }
#ifndef SIP_RUN
/**
@ -255,7 +246,6 @@ class CORE_EXPORT QgsPointCloudRenderContext
#endif
QgsRenderContext &mRenderContext;
QPainter *mElevationPainter = nullptr;
QgsVector3D mScale;
QgsVector3D mOffset;
long mPointsRendered = 0;
@ -266,7 +256,7 @@ class CORE_EXPORT QgsPointCloudRenderContext
int mZOffset = 0;
double mZValueScale = 1.0;
double mZValueFixedOffset = 0;
bool mUseElevationMap = false;
std::unique_ptr<QgsElevationMap> mElevationMap;
QgsFeedback *mFeedback = nullptr;
};

View File

@ -85,7 +85,7 @@ void QgsPointCloudRgbRenderer::renderBlock( const QgsPointCloudBlock *block, Qgs
const bool useBlueContrastEnhancement = mBlueContrastEnhancement && mBlueContrastEnhancement->contrastEnhancementAlgorithm() != QgsContrastEnhancement::NoEnhancement;
const bool useGreenContrastEnhancement = mGreenContrastEnhancement && mGreenContrastEnhancement->contrastEnhancementAlgorithm() != QgsContrastEnhancement::NoEnhancement;
const bool renderElevation = context.useElevationMap();
const bool renderElevation = context.elevationMap();
const QgsDoubleRange zRange = context.renderContext().zRange();
const bool considerZ = !zRange.isInfinite() || renderElevation;

View File

@ -0,0 +1,90 @@
/***************************************************************************
qgselevationmap.cpp
--------------------------------------
Date : August 2022
Copyright : (C) 2022 by Martin Dobias
Email : wonder dot sk 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 "qgselevationmap.h"
#include <QPainter>
#include <cmath>
const double QgsElevationMap::mZMin = -5000;
const double QgsElevationMap::mZMax = +5000;
QgsElevationMap::QgsElevationMap( const QSize &size )
: mElevationImage( size, QImage::Format_ARGB32 )
{
mElevationImage.fill( QColor::fromRgbF( 0, 0, 0, 0 ) );
mPainter.reset( new QPainter );
mPainter->begin( &mElevationImage );
}
QColor QgsElevationMap::encodeElevation( float z )
{
float zFloat = ( z - mZMin ) / ( mZMax - mZMin );
zFloat = std::clamp<float>( zFloat, 0.0, 1.0 );
int zInt = zFloat * 0x00ffffff;
QColor c;
c.setRed( ( zInt & 0xff0000 ) >> 16 );
c.setGreen( ( zInt & 0xff00 ) >> 8 );
c.setBlue( zInt & 0xff );
c.setAlphaF( 1.0f );
return c;
}
float QgsElevationMap::decodeElevation( const QRgb *colorRaw )
{
float z = ( float )( ( *colorRaw ) & 0x00ffffff ) / ( ( float ) 0x00ffffff );
z *= mZMax - mZMin;
return z;
}
void QgsElevationMap::applyEyeDomeLighting( QImage &img, int distance, float strength, float zScale )
{
int imgWidth = img.width();
uchar *imgPtr = img.bits();
const QRgb *elevPtr = reinterpret_cast<const QRgb *>( mElevationImage.constBits() );
for ( int i = distance; i < img.width() - distance; ++i )
{
for ( int j = distance; j < img.height() - distance; ++j )
{
int neighbours[] = { -1, 0, 1, 0, 0, -1, 0, 1 };
float factor = 0.0f;
float centerDepth = decodeElevation( elevPtr + ( j * imgWidth + i ) );
for ( int k = 0; k < 4; ++k )
{
int iNeighbour = i + distance * neighbours[2 * k];
int jNeighbour = j + distance * neighbours[2 * k + 1];
float neighbourDepth = decodeElevation( elevPtr + ( jNeighbour * imgWidth + iNeighbour ) );
factor += std::max<float>( 0, -( centerDepth - neighbourDepth ) );
}
factor /= zScale;
float shade = exp( -factor / 4 * strength );
uchar *imgPixel = imgPtr + ( j * imgWidth + i ) * 4;
uchar &red = *( imgPixel );
uchar &green = *( imgPixel + 1 );
uchar &blue = *( imgPixel + 2 );
red = red * shade;
green = green * shade;
blue = blue * shade;
}
}
}

View File

@ -0,0 +1,59 @@
/***************************************************************************
qgselevationmap.h
--------------------------------------
Date : August 2022
Copyright : (C) 2022 by Martin Dobias
Email : wonder dot sk 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 QGSELEVATIONMAP_H
#define QGSELEVATIONMAP_H
#include "qgis.h"
#include "qgis_sip.h"
#include <QImage>
#include <memory>
/**
* Stores digital elevation model in a raster image which may get updated
* as a part of map layer rendering process. Afterwards the elevations can
* be used for post-processing effects of the rendered color map image.
*
* \since QGIS 3.28
*/
class CORE_EXPORT QgsElevationMap
{
public:
explicit QgsElevationMap( const QSize &size );
/**
* Applies eye dome lighting effect to the given image.
*/
void applyEyeDomeLighting( QImage &img, int distance, float strength, float zScale );
//! Returns painter to the underlying QImage with elevations
QPainter *painter() { return mPainter.get(); }
//! Converts elevation value to an actual color
static QColor encodeElevation( float z );
//! Converts a color back to elevation value
static float decodeElevation( const QRgb *colorRaw );
private:
static const double mZMin;
static const double mZMax;
QImage mElevationImage;
std::unique_ptr<QPainter> mPainter;
};
#endif // QGSELEVATIONMAP_H