mirror of
https://github.com/qgis/QGIS.git
synced 2025-02-25 00:58:06 -05:00
Refactor elevation map related code to a new class QgsElevationMap
This commit is contained in:
parent
8f933496b6
commit
18b3bdd139
@ -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 );
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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;
|
||||
|
||||
|
90
src/core/qgselevationmap.cpp
Normal file
90
src/core/qgselevationmap.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
59
src/core/qgselevationmap.h
Normal file
59
src/core/qgselevationmap.h
Normal 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
|
Loading…
x
Reference in New Issue
Block a user