29#include <Qt3DRender/QGeometryRenderer>
30#include <Qt3DCore/QTransform>
31#include <QMutexLocker>
35static void _heightMapMinMax(
const QByteArray &heightMap,
float &zMin,
float &zMax )
37 const float *zBits = (
const float * ) heightMap.constData();
38 int zCount = heightMap.count() /
sizeof( float );
41 zMin = zMax = std::numeric_limits<float>::quiet_NaN();
42 for (
int i = 0; i < zCount; ++i )
45 if ( std::isnan( z ) )
52 zMin = std::min( zMin, z );
53 zMax = std::max( zMax, z );
58QgsDemTerrainTileLoader::QgsDemTerrainTileLoader( QgsTerrainEntity *terrain, QgsChunkNode *node,
QgsTerrainGenerator *terrainGenerator )
59 : QgsTerrainTileLoader( terrain, node )
63 QgsDemHeightMapGenerator *heightMapGenerator =
nullptr;
80 connect( heightMapGenerator, &QgsDemHeightMapGenerator::heightMapReady,
this, &QgsDemTerrainTileLoader::onHeightMapReady );
81 mHeightMapJobId = heightMapGenerator->render( node->tileId() );
82 mResolution = heightMapGenerator->resolution();
85Qt3DCore::QEntity *QgsDemTerrainTileLoader::createEntity( Qt3DCore::QEntity *parent )
88 _heightMapMinMax( mHeightMap, zMin, zMax );
90 if ( std::isnan( zMin ) || std::isnan( zMax ) )
97 QgsChunkNodeId nodeId = mNode->tileId();
101 double side = extent.
width();
102 double half = side / 2;
105 QgsTerrainTileEntity *entity =
new QgsTerrainTileEntity( nodeId );
109 Qt3DRender::QGeometryRenderer *mesh =
new Qt3DRender::QGeometryRenderer;
110 mesh->setGeometry(
new DemTerrainTileGeometry( mResolution, side, map.
terrainVerticalScale(), mSkirtHeight, mHeightMap, mesh ) );
111 entity->addComponent( mesh );
119 Qt3DCore::QTransform *transform =
nullptr;
120 transform =
new Qt3DCore::QTransform();
121 entity->addComponent( transform );
123 transform->setScale( side );
124 transform->setTranslation( QVector3D( x0 + half, 0, - ( y0 + half ) ) );
127 mNode->updateParentBoundingBoxesRecursively();
129 entity->setEnabled(
false );
130 entity->setParent( parent );
134void QgsDemTerrainTileLoader::onHeightMapReady(
int jobId,
const QByteArray &heightMap )
136 if ( mHeightMapJobId == jobId )
138 this->mHeightMap = heightMap;
139 mHeightMapJobId = -1;
151#include <QtConcurrent/QtConcurrentRun>
152#include <QFutureWatcher>
157 , mClonedProvider( dtm ? qgis::down_cast<
QgsRasterDataProvider *>( dtm->dataProvider()->clone() ) : nullptr )
158 , mTilingScheme( tilingScheme )
159 , mResolution( resolution )
162 , mTransformContext( transformContext )
166QgsDemHeightMapGenerator::~QgsDemHeightMapGenerator()
168 delete mClonedProvider;
174 QgsEventTracing::ScopedEvent e( QStringLiteral(
"3D" ), QStringLiteral(
"DEM" ) );
178 std::unique_ptr<QgsRasterProjector> projector;
179 if ( provider->
crs() != destCrs )
184 input = projector.get();
186 std::unique_ptr< QgsRasterBlock > block( input->
block( 1, extent, res, res ) );
192 data = block->data();
195 if ( block->hasNoData() )
198 float *floatData =
reinterpret_cast<float *
>( data.data() );
199 Q_ASSERT( data.count() %
sizeof(
float ) == 0 );
200 int count = data.count() /
sizeof( float );
201 for (
int i = 0; i < count; ++i )
203 if ( block->isNoData( i ) )
204 floatData[i] = std::numeric_limits<float>::quiet_NaN();
213 return downloader->
getHeightMap( extent, res, destCrs, context );
216int QgsDemHeightMapGenerator::render(
const QgsChunkNodeId &nodeId )
218 QgsEventTracing::addEvent( QgsEventTracing::AsyncBegin, QStringLiteral(
"3D" ), QStringLiteral(
"DEM" ), nodeId.text() );
221 QgsRectangle extent = mTilingScheme.tileToExtent( nodeId );
222 float mapUnitsPerPixel = extent.
width() / mResolution;
223 extent.
grow( mapUnitsPerPixel / 2 );
225 QgsRectangle fullExtent = mTilingScheme.tileToExtent( 0, 0, 0 );
229 jd.jobId = ++mLastJobId;
233 QFutureWatcher<QByteArray> *fw =
new QFutureWatcher<QByteArray>(
nullptr );
234 connect( fw, &QFutureWatcher<QByteArray>::finished,
this, &QgsDemHeightMapGenerator::onFutureFinished );
235 connect( fw, &QFutureWatcher<QByteArray>::finished, fw, &QObject::deleteLater );
238 jd.future = QtConcurrent::run( _readDtmData, mClonedProvider, extent, mResolution, mTilingScheme.crs() );
240 jd.future = QtConcurrent::run( _readOnlineDtm, mDownloader.get(), extent, mResolution, mTilingScheme.crs(), mTransformContext );
242 fw->setFuture( jd.future );
244 mJobs.insert( fw, jd );
249void QgsDemHeightMapGenerator::waitForFinished()
251 for ( QFutureWatcher<QByteArray> *fw : mJobs.keys() )
253 disconnect( fw, &QFutureWatcher<QByteArray>::finished,
this, &QgsDemHeightMapGenerator::onFutureFinished );
254 disconnect( fw, &QFutureWatcher<QByteArray>::finished, fw, &QObject::deleteLater );
256 QVector<QFutureWatcher<QByteArray>*> toBeDeleted;
257 for ( QFutureWatcher<QByteArray> *fw : mJobs.keys() )
259 fw->waitForFinished();
260 JobData jobData = mJobs.value( fw );
261 toBeDeleted.push_back( fw );
263 QByteArray data = jobData.future.result();
264 emit heightMapReady( jobData.jobId, data );
267 for ( QFutureWatcher<QByteArray> *fw : toBeDeleted )
274void QgsDemHeightMapGenerator::lazyLoadDtmCoarseData(
int res,
const QgsRectangle &rect )
276 QMutexLocker locker( &mLazyLoadDtmCoarseDataMutex );
277 if ( mDtmCoarseData.isEmpty() )
279 std::unique_ptr< QgsRasterBlock > block( mDtm->dataProvider()->block( 1, rect, res, res ) );
281 mDtmCoarseData = block->data();
282 mDtmCoarseData.detach();
286float QgsDemHeightMapGenerator::heightAt(
double x,
double y )
294 lazyLoadDtmCoarseData( res, rect );
296 int cellX = ( int )( ( x - rect.
xMinimum() ) / rect.
width() * res + .5f );
297 int cellY = ( int )( ( rect.
yMaximum() - y ) / rect.
height() * res + .5f );
298 cellX = std::clamp( cellX, 0, res - 1 );
299 cellY = std::clamp( cellY, 0, res - 1 );
301 const float *data = (
const float * ) mDtmCoarseData.constData();
302 return data[cellX + cellY * res];
305void QgsDemHeightMapGenerator::onFutureFinished()
307 QFutureWatcher<QByteArray> *fw =
static_cast<QFutureWatcher<QByteArray>*
>( sender() );
309 Q_ASSERT( mJobs.contains( fw ) );
310 JobData jobData = mJobs.value( fw );
315 QgsEventTracing::addEvent( QgsEventTracing::AsyncEnd, QStringLiteral(
"3D" ), QStringLiteral(
"DEM" ), jobData.tileId.text() );
317 QByteArray data = jobData.future.result();
318 emit heightMapReady( jobData.jobId, data );
@ Float32
Thirty two bit floating point (float)
double terrainVerticalScale() const
Returns vertical scale (exaggeration) of terrain.
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.
QgsVector3D origin() const
Returns coordinates in map CRS at which 3D scene has origin (0,0,0)
This class 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.
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 ...
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 ...
Base class for raster data providers.
bool setInput(QgsRasterInterface *input) override
Set input.
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.
QgsRasterProjector implements approximate projection support for it calculates grid of points in sour...
A rectangle specified with double values.
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
void grow(double delta)
Grows the rectangle in place by the specified amount.
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
QgsRectangle intersect(const QgsRectangle &rect) const
Returns the intersection with the given rectangle.
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,...
@ Dem
Terrain is built from raster layer with digital elevation model.
@ Online
Terrain is built from downloaded tiles with digital elevation model.
virtual Type type() const =0
What texture generator implementation is this.
const QgsTilingScheme & tilingScheme() const
Returns tiling scheme of the terrain.
QgsRectangle tileToExtent(int x, int y, int z) const
Returns map coordinates of the extent of a tile.
double y() const
Returns Y coordinate.
double x() const
Returns X coordinate.