29 #include <Qt3DRender/QGeometryRenderer>
30 #include <Qt3DCore/QTransform>
31 #include <QMutexLocker>
35 static 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 );
58 QgsDemTerrainTileLoader::QgsDemTerrainTileLoader( QgsTerrainEntity *terrain, QgsChunkNode *node,
QgsTerrainGenerator *terrainGenerator )
59 : QgsTerrainTileLoader( terrain, node )
80 connect(
heightMapGenerator, &QgsDemHeightMapGenerator::heightMapReady,
this, &QgsDemTerrainTileLoader::onHeightMapReady );
85 Qt3DCore::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 );
134 void 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 )
166 QgsDemHeightMapGenerator::~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 );
216 int 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 );
249 void 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 )
274 void 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();
286 float 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];
305 void 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 );