31#include <QMutexLocker>
32#include <Qt3DCore/QTransform>
33#include <Qt3DRender/QGeometryRenderer>
35#include "moc_qgsdemterraintileloader_p.cpp"
39static void _heightMapMinMax(
const QByteArray &heightMap,
float &zMin,
float &zMax )
41 const float *zBits = (
const float * ) heightMap.constData();
42 int zCount = heightMap.count() /
sizeof( float );
45 zMin = zMax = std::numeric_limits<float>::quiet_NaN();
46 for (
int i = 0; i < zCount; ++i )
49 if ( std::isnan( z ) )
56 zMin = std::min( zMin, z );
57 zMax = std::max( zMax, z );
62QgsDemTerrainTileLoader::QgsDemTerrainTileLoader( QgsTerrainEntity *terrain, QgsChunkNode *node,
QgsTerrainGenerator *terrainGenerator )
63 : QgsTerrainTileLoader( terrain, node )
64 , mTerrainGenerator( terrainGenerator )
67void QgsDemTerrainTileLoader::start()
69 QgsChunkNode *node = chunk();
71 QgsDemHeightMapGenerator *heightMapGenerator =
nullptr;
88 connect( heightMapGenerator, &QgsDemHeightMapGenerator::heightMapReady,
this, &QgsDemTerrainTileLoader::onHeightMapReady );
89 mHeightMapJobId = heightMapGenerator->render( node->tileId() );
90 mResolution = heightMapGenerator->resolution();
93Qt3DCore::QEntity *QgsDemTerrainTileLoader::createEntity( Qt3DCore::QEntity *parent )
96 _heightMapMinMax( mHeightMap, zMin, zMax );
98 if ( std::isnan( zMin ) || std::isnan( zMax ) )
105 QgsChunkNodeId nodeId = mNode->tileId();
107 double side = extent.
width();
109 QgsTerrainTileEntity *entity =
new QgsTerrainTileEntity( nodeId );
113 Qt3DRender::QGeometryRenderer *mesh =
new Qt3DRender::QGeometryRenderer;
114 mesh->setGeometry(
new DemTerrainTileGeometry( mResolution, side, map->
terrainSettings()->
verticalScale(), mSkirtHeight, mHeightMap, mesh ) );
115 entity->addComponent( mesh );
122 QgsGeoTransform *transform =
new QgsGeoTransform;
124 entity->addComponent( transform );
127 mNode->updateParentBoundingBoxesRecursively();
129 entity->setParent( parent );
133void QgsDemTerrainTileLoader::onHeightMapReady(
int jobId,
const QByteArray &heightMap )
135 if ( mHeightMapJobId == jobId )
137 this->mHeightMap = heightMap;
138 mHeightMapJobId = -1;
150#include <QtConcurrent/QtConcurrentRun>
151#include <QFutureWatcher>
157 , mClonedProvider( dtm ? qgis::down_cast<
QgsRasterDataProvider *>( dtm->dataProvider()->clone() ) : nullptr )
158 , mTilingScheme( tilingScheme )
159 , mResolution( resolution )
161 , mTransformContext( transformContext )
165QgsDemHeightMapGenerator::~QgsDemHeightMapGenerator()
167 delete mClonedProvider;
173 provider->moveToThread( QThread::currentThread() );
175 QgsEventTracing::ScopedEvent e( QStringLiteral(
"3D" ), QStringLiteral(
"DEM" ) );
179 std::unique_ptr<QgsRasterProjector> projector;
180 if ( provider->
crs() != destCrs )
182 projector = std::make_unique<QgsRasterProjector>();
184 projector->setInput( provider );
185 input = projector.get();
187 std::unique_ptr<QgsRasterBlock> block( input->
block( 1, extent, res, res ) );
196 if ( !block->hasNoDataValue() )
200 block->setNoDataValue( std::numeric_limits<float>::lowest() );
202 block->setIsNoDataExcept( subRect );
204 data = block->data();
207 if ( block->hasNoData() )
210 float *floatData =
reinterpret_cast<float *
>( data.data() );
211 Q_ASSERT( data.count() %
sizeof(
float ) == 0 );
212 int count = data.count() /
sizeof( float );
213 for (
int i = 0; i < count; ++i )
215 if ( block->isNoData( i ) )
216 floatData[i] = std::numeric_limits<float>::quiet_NaN();
227 return downloader->
getHeightMap( extent, res, destCrs, context );
230int QgsDemHeightMapGenerator::render(
const QgsChunkNodeId &nodeId )
232 QgsEventTracing::addEvent( QgsEventTracing::AsyncBegin, QStringLiteral(
"3D" ), QStringLiteral(
"DEM" ), nodeId.text() );
235 QgsRectangle extent = mTilingScheme.tileToExtent( nodeId );
236 float mapUnitsPerPixel = extent.
width() / mResolution;
237 extent.
grow( mapUnitsPerPixel / 2 );
239 QgsRectangle rootTileExtent = mTilingScheme.tileToExtent( 0, 0, 0 );
240 extent = extent.
intersect( rootTileExtent );
243 jd.jobId = ++mLastJobId;
247 QFutureWatcher<QByteArray> *fw =
new QFutureWatcher<QByteArray>(
nullptr );
248 connect( fw, &QFutureWatcher<QByteArray>::finished,
this, &QgsDemHeightMapGenerator::onFutureFinished );
249 connect( fw, &QFutureWatcher<QByteArray>::finished, fw, &QObject::deleteLater );
250 if ( mClonedProvider )
253 std::unique_ptr<QgsRasterDataProvider> clonedProviderClone( mClonedProvider->clone() );
254 clonedProviderClone->moveToThread(
nullptr );
255 jd.future = QtConcurrent::run( _readDtmData, clonedProviderClone.release(), extent, mResolution, mTilingScheme.crs(), mTilingScheme.fullExtent() );
259 jd.future = QtConcurrent::run( _readOnlineDtm, mDownloader.get(), extent, mResolution, mTilingScheme.crs(), mTransformContext );
262 fw->setFuture( jd.future );
264 mJobs.insert( fw, jd );
269void QgsDemHeightMapGenerator::waitForFinished()
271 for (
auto it = mJobs.keyBegin(); it != mJobs.keyEnd(); it++ )
273 QFutureWatcher<QByteArray> *fw = *it;
274 disconnect( fw, &QFutureWatcher<QByteArray>::finished,
this, &QgsDemHeightMapGenerator::onFutureFinished );
275 disconnect( fw, &QFutureWatcher<QByteArray>::finished, fw, &QObject::deleteLater );
277 QVector<QFutureWatcher<QByteArray> *> toBeDeleted;
278 for (
auto it = mJobs.keyBegin(); it != mJobs.keyEnd(); it++ )
280 QFutureWatcher<QByteArray> *fw = *it;
281 fw->waitForFinished();
282 JobData jobData = mJobs.value( fw );
283 toBeDeleted.push_back( fw );
285 QByteArray data = jobData.future.result();
286 emit heightMapReady( jobData.jobId, data );
289 for ( QFutureWatcher<QByteArray> *fw : toBeDeleted )
296void QgsDemHeightMapGenerator::lazyLoadDtmCoarseData(
int res,
const QgsRectangle &rect )
298 QMutexLocker locker( &mLazyLoadDtmCoarseDataMutex );
299 if ( mDtmCoarseData.isEmpty() )
301 std::unique_ptr<QgsRasterBlock> block( mClonedProvider->block( 1, rect, res, res ) );
303 mDtmCoarseData = block->data();
304 mDtmCoarseData.detach();
308float QgsDemHeightMapGenerator::heightAt(
double x,
double y )
310 if ( !mClonedProvider )
315 lazyLoadDtmCoarseData( res, mDtmExtent );
317 int cellX = ( int ) ( ( x - mDtmExtent.xMinimum() ) / mDtmExtent.width() * res + .5f );
318 int cellY = ( int ) ( ( mDtmExtent.yMaximum() - y ) / mDtmExtent.height() * res + .5f );
319 cellX = std::clamp( cellX, 0, res - 1 );
320 cellY = std::clamp( cellY, 0, res - 1 );
322 const float *data = (
const float * ) mDtmCoarseData.constData();
323 return data[cellX + cellY * res];
326void QgsDemHeightMapGenerator::onFutureFinished()
328 QFutureWatcher<QByteArray> *fw =
static_cast<QFutureWatcher<QByteArray> *
>( sender() );
330 Q_ASSERT( mJobs.contains( fw ) );
331 JobData jobData = mJobs.value( fw );
336 QgsEventTracing::addEvent( QgsEventTracing::AsyncEnd, QStringLiteral(
"3D" ), QStringLiteral(
"DEM" ), jobData.tileId.text() );
338 QByteArray data = jobData.future.result();
339 emit heightMapReady( jobData.jobId, data );
@ Float32
Thirty two bit floating point (float).
const QgsAbstractTerrainSettings * terrainSettings() const
Returns the terrain settings.
QgsTerrainGenerator * terrainGenerator() const
Returns the terrain generator.
bool isTerrainShadingEnabled() const
Returns whether terrain shading is enabled.
QgsPhongMaterialSettings terrainShadingMaterial() const
Returns terrain shading material.
QList< QgsMapLayer * > layers() const
Returns the list of 3D map layers to be rendered in the scene.
double verticalScale() const
Returns the vertical scale (exaggeration) for terrain.
A 3-dimensional box composed of x, y, z coordinates.
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.
Implementation of terrain generator that uses a raster layer with DEM to build terrain.
QgsDemHeightMapGenerator * heightMapGenerator()
Returns height map generator object - takes care of extraction of elevations from the layer).
float skirtHeight() const
Returns skirt height (in world units). Skirts at the edges of terrain tiles help hide cracks between ...
Implementation of terrain generator that uses online resources to download heightmaps.
QgsDemHeightMapGenerator * heightMapGenerator()
Returns height map generator object - takes care of extraction of elevations from the layer).
float skirtHeight() const
Returns skirt height (in world units). Skirts at the edges of terrain tiles help hide cracks between ...
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,...
Base class for generators of terrain.
@ Dem
Terrain is built from raster layer with digital elevation model.
@ Online
Terrain is built from downloaded tiles with digital elevation model.
const QgsTilingScheme & tilingScheme() const
Returns tiling scheme of the terrain.
Encapsulates tiling schemes (just like with WMTS / TMS / XYZ layers).
QgsRectangle tileToExtent(int x, int y, int z) const
Returns map coordinates of the extent of a tile.
A 3D vector (similar to QVector3D) with the difference that it uses double precision instead of singl...