29 #include <Qt3DRender/QGeometryRenderer>
30 #include <QMutexLocker>
34 static void _heightMapMinMax(
const QByteArray &heightMap,
float &zMin,
float &zMax )
36 const float *zBits = (
const float * ) heightMap.constData();
37 int zCount = heightMap.count() /
sizeof( float );
40 zMin = zMax = std::numeric_limits<float>::quiet_NaN();
41 for (
int i = 0; i < zCount; ++i )
44 if ( std::isnan( z ) )
51 zMin = std::min( zMin, z );
52 zMax = std::max( zMax, z );
57 QgsDemTerrainTileLoader::QgsDemTerrainTileLoader( QgsTerrainEntity *terrain, QgsChunkNode *node,
QgsTerrainGenerator *terrainGenerator )
58 : QgsTerrainTileLoader( terrain, node )
79 connect(
heightMapGenerator, &QgsDemHeightMapGenerator::heightMapReady,
this, &QgsDemTerrainTileLoader::onHeightMapReady );
80 mHeightMapJobId =
heightMapGenerator->render( node->tileX(), node->tileY(), node->tileZ() );
84 Qt3DCore::QEntity *QgsDemTerrainTileLoader::createEntity( Qt3DCore::QEntity *parent )
87 _heightMapMinMax( mHeightMap, zMin, zMax );
89 if ( std::isnan( zMin ) || std::isnan( zMax ) )
99 double side = extent.
width();
100 double half = side / 2;
103 QgsTerrainTileEntity *entity =
new QgsTerrainTileEntity( mNode->tileId() );
107 Qt3DRender::QGeometryRenderer *mesh =
new Qt3DRender::QGeometryRenderer;
108 mesh->setGeometry(
new DemTerrainTileGeometry( mResolution, side, map.
terrainVerticalScale(), mSkirtHeight, mHeightMap, mesh ) );
109 entity->addComponent( mesh );
117 Qt3DCore::QTransform *transform =
nullptr;
118 transform =
new Qt3DCore::QTransform();
119 entity->addComponent( transform );
121 transform->setScale( side );
122 transform->setTranslation( QVector3D( x0 + half, 0, - ( y0 + half ) ) );
126 entity->setEnabled(
false );
127 entity->setParent( parent );
131 void QgsDemTerrainTileLoader::onHeightMapReady(
int jobId,
const QByteArray &heightMap )
133 if ( mHeightMapJobId == jobId )
135 this->mHeightMap = heightMap;
136 mHeightMapJobId = -1;
148 #include <QtConcurrent/QtConcurrentRun>
149 #include <QFutureWatcher>
155 , mTilingScheme( tilingScheme )
156 , mResolution( resolution )
162 QgsDemHeightMapGenerator::~QgsDemHeightMapGenerator()
164 delete mClonedProvider;
170 QgsEventTracing::ScopedEvent e( QStringLiteral(
"3D" ), QStringLiteral(
"DEM" ) );
174 std::unique_ptr<QgsRasterProjector> projector;
175 if ( provider->
crs() != destCrs )
180 input = projector.get();
182 std::unique_ptr< QgsRasterBlock > block( input->
block( 1, extent, res, res ) );
188 data = block->data();
191 if ( block->hasNoData() )
194 float *floatData =
reinterpret_cast<float *
>( data.data() );
195 Q_ASSERT( data.count() %
sizeof(
float ) == 0 );
196 int count = data.count() /
sizeof( float );
197 for (
int i = 0; i < count; ++i )
199 if ( block->isNoData( i ) )
200 floatData[i] = std::numeric_limits<float>::quiet_NaN();
209 return downloader->
getHeightMap( extent, res, destCrs );
212 int QgsDemHeightMapGenerator::render(
int x,
int y,
int z )
214 QgsChunkNodeId tileId( x, y, z );
216 QgsEventTracing::addEvent( QgsEventTracing::AsyncBegin, QStringLiteral(
"3D" ), QStringLiteral(
"DEM" ), tileId.text() );
219 QgsRectangle extent = mTilingScheme.tileToExtent( x, y, z );
220 float mapUnitsPerPixel = extent.
width() / mResolution;
221 extent.
grow( mapUnitsPerPixel / 2 );
223 QgsRectangle fullExtent = mTilingScheme.tileToExtent( 0, 0, 0 );
227 jd.jobId = ++mLastJobId;
233 jd.future = QtConcurrent::run( _readDtmData, mClonedProvider, extent, mResolution, mTilingScheme.crs() );
235 jd.future = QtConcurrent::run( _readOnlineDtm, mDownloader.get(), extent, mResolution, mTilingScheme.crs() );
237 QFutureWatcher<QByteArray> *fw =
new QFutureWatcher<QByteArray>(
nullptr );
238 fw->setFuture( jd.future );
239 connect( fw, &QFutureWatcher<QByteArray>::finished,
this, &QgsDemHeightMapGenerator::onFutureFinished );
240 connect( fw, &QFutureWatcher<QByteArray>::finished, fw, &QObject::deleteLater );
242 mJobs.insert( fw, jd );
247 void QgsDemHeightMapGenerator::waitForFinished()
249 for ( QFutureWatcher<QByteArray> *fw : mJobs.keys() )
251 disconnect( fw, &QFutureWatcher<QByteArray>::finished,
this, &QgsDemHeightMapGenerator::onFutureFinished );
252 disconnect( fw, &QFutureWatcher<QByteArray>::finished, fw, &QObject::deleteLater );
254 QVector<QFutureWatcher<QByteArray>*> toBeDeleted;
255 for ( QFutureWatcher<QByteArray> *fw : mJobs.keys() )
257 fw->waitForFinished();
258 JobData jobData = mJobs.value( fw );
259 toBeDeleted.push_back( fw );
261 QByteArray data = jobData.future.result();
262 emit heightMapReady( jobData.jobId, data );
265 for ( QFutureWatcher<QByteArray> *fw : toBeDeleted )
272 void QgsDemHeightMapGenerator::lazyLoadDtmCoarseData(
int res,
const QgsRectangle &rect )
274 QMutexLocker locker( &mLazyLoadDtmCoarseDataMutex );
275 if ( mDtmCoarseData.isEmpty() )
277 std::unique_ptr< QgsRasterBlock > block( mDtm->dataProvider()->block( 1, rect, res, res ) );
279 mDtmCoarseData = block->data();
280 mDtmCoarseData.detach();
284 float QgsDemHeightMapGenerator::heightAt(
double x,
double y )
292 lazyLoadDtmCoarseData( res, rect );
294 int cellX = ( int )( ( x - rect.
xMinimum() ) / rect.
width() * res + .5f );
295 int cellY = ( int )( ( rect.
yMaximum() - y ) / rect.
height() * res + .5f );
296 cellX = qBound( 0, cellX, res - 1 );
297 cellY = qBound( 0, cellY, res - 1 );
299 const float *data = (
const float * ) mDtmCoarseData.constData();
300 return data[cellX + cellY * res];
303 void QgsDemHeightMapGenerator::onFutureFinished()
305 QFutureWatcher<QByteArray> *fw =
static_cast<QFutureWatcher<QByteArray>*
>( sender() );
307 Q_ASSERT( mJobs.contains( fw ) );
308 JobData jobData = mJobs.value( fw );
313 QgsEventTracing::addEvent( QgsEventTracing::AsyncEnd, QStringLiteral(
"3D" ), QStringLiteral(
"DEM" ), jobData.tileId.text() );
315 QByteArray data = jobData.future.result();
316 emit heightMapReady( jobData.jobId, data );