32#include <QFutureWatcher>
33#include <QMutexLocker>
35#include <Qt3DCore/QTransform>
36#include <Qt3DRender/QGeometryRenderer>
37#include <QtConcurrentRun>
39#include "moc_qgsdemterraintileloader_p.cpp"
41using namespace Qt::StringLiterals;
47 , mClonedProvider( dtm ? qgis::down_cast<
QgsRasterDataProvider *>( dtm->dataProvider()->clone() ) : nullptr )
48 , mTilingScheme( tilingScheme )
49 , mResolution( resolution )
51 , mTransformContext( transformContext )
54QgsDemHeightMapGenerator::~QgsDemHeightMapGenerator()
60 provider->moveToThread( QThread::currentThread() );
62 QgsScopedEvent e( u
"3D"_s, u
"DEM"_s );
66 std::unique_ptr<QgsRasterProjector> projector;
67 if ( provider->
crs() != destCrs )
69 projector = std::make_unique<QgsRasterProjector>();
71 projector->setInput( provider );
72 input = projector.get();
74 std::unique_ptr<QgsRasterBlock> block( input->
block( 1, extent, res, res ) );
83 if ( !block->hasNoDataValue() )
87 block->setNoDataValue( std::numeric_limits<float>::lowest() );
89 block->setIsNoDataExcept( subRect );
94 if ( block->hasNoData() )
97 float *floatData =
reinterpret_cast<float *
>( data.data() );
98 Q_ASSERT( data.count() %
sizeof(
float ) == 0 );
99 int count = data.count() /
sizeof( float );
100 for (
int i = 0; i < count; ++i )
102 if ( block->isNoData( i ) )
103 floatData[i] = std::numeric_limits<float>::quiet_NaN();
114 return downloader->
getHeightMap( extent, res, destCrs, context );
117QFuture<QByteArray> QgsDemHeightMapGenerator::render(
const QgsChunkNodeId &nodeId )
119 QgsEventTracing::addEvent( QgsEventTracing::AsyncBegin, u
"3D"_s, u
"DEM"_s, nodeId.text() );
122 QgsRectangle extent = mTilingScheme.tileToExtent( nodeId );
123 float mapUnitsPerPixel = extent.
width() / mResolution;
124 extent.
grow( mapUnitsPerPixel / 2 );
126 QgsRectangle rootTileExtent = mTilingScheme.tileToExtent( 0, 0, 0 );
127 extent = extent.
intersect( rootTileExtent );
129 QFutureWatcher<QByteArray> *fw =
new QFutureWatcher<QByteArray>(
nullptr );
130 connect( fw, &QFutureWatcher<QByteArray>::finished,
this, [
this, fw]() { mJobs.remove( fw ); } );
131 connect( fw, &QFutureWatcher<QByteArray>::finished, fw, &QObject::deleteLater );
133 QFuture<QByteArray> future;
134 if ( mClonedProvider )
137 std::unique_ptr<QgsRasterDataProvider> clonedProviderClone( mClonedProvider->clone() );
138 clonedProviderClone->moveToThread(
nullptr );
139 future = QtConcurrent::run( readDtmData, clonedProviderClone.release(), extent, mResolution, mTilingScheme.crs(), mTilingScheme.fullExtent() );
143 future = QtConcurrent::run( readOnlineDtm, mDownloader.get(), extent, mResolution, mTilingScheme.crs(), mTransformContext );
146 fw->setFuture( future );
150void QgsDemHeightMapGenerator::waitForFinished()
152 QVector<QFutureWatcher<QByteArray> *> toBeDeleted;
153 for (
auto it = mJobs.keyBegin(); it != mJobs.keyEnd(); it++ )
155 toBeDeleted.push_back( *it );
158 for ( QFutureWatcher<QByteArray> *fw : toBeDeleted )
160 fw->waitForFinished();
164void QgsDemHeightMapGenerator::lazyLoadDtmCoarseData(
int res,
const QgsRectangle &rect )
166 QMutexLocker locker( &mLazyLoadDtmCoarseDataMutex );
167 if ( !mDtmCoarseRasterBlock )
169 mDtmCoarseRasterBlock.reset( mClonedProvider->block( 1, rect, res, res ) );
173float QgsDemHeightMapGenerator::heightAt(
double x,
double y )
175 if ( !mClonedProvider )
176 return std::numeric_limits<float>::quiet_NaN();
180 lazyLoadDtmCoarseData( res, mDtmExtent );
182 int cellX = ( int ) ( ( x - mDtmExtent.xMinimum() ) / mDtmExtent.width() * res + .5f );
183 int cellY = ( int ) ( ( mDtmExtent.yMaximum() - y ) / mDtmExtent.height() * res + .5f );
184 cellX = std::clamp( cellX, 0, res - 1 );
185 cellY = std::clamp( cellY, 0, res - 1 );
187 bool isNoData =
false;
188 const double val = mDtmCoarseRasterBlock->valueAndNoData( cellY, cellX, isNoData );
190 return isNoData ? std::numeric_limits<float>::quiet_NaN() : static_cast<float>( val );
@ Float32
Thirty two bit floating point (float).
Represents a coordinate reference system (CRS).
Contains information about the context in which a coordinate transform is executed.
QgsCoordinateTransformContext transformContext() const
Returns data provider coordinate transform context.
virtual QgsCoordinateReferenceSystem crs() const =0
Returns the coordinate system for the data source.
static QRect subRect(const QgsRectangle &extent, int width, int height, const QgsRectangle &subExtent)
For extent and width, height find rectangle covered by subextent.
Base class for raster data providers.
Base class for processing filters like renderers, reprojector, resampler etc.
virtual QgsRasterBlock * block(int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback *feedback=nullptr)=0
Read block of data using given extent and size.
Represents a raster layer.
A rectangle specified with double values.
void grow(double delta)
Grows the rectangle in place by the specified amount.
QgsRectangle intersect(const QgsRectangle &rect) const
Returns the intersection with the given rectangle.
Takes care of downloading terrain data from a publicly available data source.
QByteArray getHeightMap(const QgsRectangle &extentOrig, int res, const QgsCoordinateReferenceSystem &destCrs, const QgsCoordinateTransformContext &context=QgsCoordinateTransformContext(), QString tmpFilenameImg=QString(), QString tmpFilenameTif=QString())
For given extent and resolution (number of pixels for width/height) in specified CRS,...
Encapsulates tiling schemes (just like with WMTS / TMS / XYZ layers).