28 #include <Qt3DRender/QGeometryRenderer>
32 static void _heightMapMinMax(
const QByteArray &heightMap,
float &zMin,
float &zMax )
34 const float *zBits = (
const float * ) heightMap.constData();
35 int zCount = heightMap.count() /
sizeof( float );
38 zMin = zMax = std::numeric_limits<float>::quiet_NaN();
39 for (
int i = 0; i < zCount; ++i )
42 if ( std::isnan( z ) )
49 zMin = std::min( zMin, z );
50 zMax = std::max( zMax, z );
55 QgsDemTerrainTileLoader::QgsDemTerrainTileLoader( QgsTerrainEntity *terrain, QgsChunkNode *node )
56 : 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();
210 return downloader->
getHeightMap( extent, res, destCrs );
213 int QgsDemHeightMapGenerator::render(
int x,
int y,
int z )
215 QgsChunkNodeId tileId( x, y, z );
217 QgsEventTracing::addEvent( QgsEventTracing::AsyncBegin, QStringLiteral(
"3D" ), QStringLiteral(
"DEM" ), tileId.text() );
220 QgsRectangle extent = mTilingScheme.tileToExtent( x, y, z );
221 float mapUnitsPerPixel = extent.
width() / mResolution;
222 extent.
grow( mapUnitsPerPixel / 2 );
224 QgsRectangle fullExtent = mTilingScheme.tileToExtent( 0, 0, 0 );
228 jd.jobId = ++mLastJobId;
234 jd.future = QtConcurrent::run( _readDtmData, mClonedProvider, extent, mResolution, mTilingScheme.crs() );
236 jd.future = QtConcurrent::run( _readOnlineDtm, mDownloader.get(), extent, mResolution, mTilingScheme.crs() );
238 QFutureWatcher<QByteArray> *fw =
new QFutureWatcher<QByteArray>(
nullptr );
239 fw->setFuture( jd.future );
240 connect( fw, &QFutureWatcher<QByteArray>::finished,
this, &QgsDemHeightMapGenerator::onFutureFinished );
241 connect( fw, &QFutureWatcher<QByteArray>::finished, fw, &QObject::deleteLater );
243 mJobs.insert( fw, jd );
248 QByteArray QgsDemHeightMapGenerator::renderSynchronously(
int x,
int y,
int z )
251 QgsRectangle extent = mTilingScheme.tileToExtent( x, y, z );
252 float mapUnitsPerPixel = extent.
width() / mResolution;
253 extent.
grow( mapUnitsPerPixel / 2 );
255 QgsRectangle fullExtent = mTilingScheme.tileToExtent( 0, 0, 0 );
258 std::unique_ptr< QgsRasterBlock > block( mDtm->dataProvider()->block( 1, extent, mResolution, mResolution ) );
264 data = block->data();
271 float QgsDemHeightMapGenerator::heightAt(
double x,
double y )
279 if ( mDtmCoarseData.isEmpty() )
281 std::unique_ptr< QgsRasterBlock > block( mDtm->dataProvider()->block( 1, rect, res, res ) );
283 mDtmCoarseData = block->data();
284 mDtmCoarseData.detach();
287 int cellX = ( int )( ( x - rect.
xMinimum() ) / rect.
width() * res + .5f );
288 int cellY = ( int )( ( rect.
yMaximum() - y ) / rect.
height() * res + .5f );
289 cellX = qBound( 0, cellX, res - 1 );
290 cellY = qBound( 0, cellY, res - 1 );
292 const float *data = (
const float * ) mDtmCoarseData.constData();
293 return data[cellX + cellY * res];
296 void QgsDemHeightMapGenerator::onFutureFinished()
298 QFutureWatcher<QByteArray> *fw =
static_cast<QFutureWatcher<QByteArray>*
>( sender() );
300 Q_ASSERT( mJobs.contains( fw ) );
301 JobData jobData = mJobs.value( fw );
306 QgsEventTracing::addEvent( QgsEventTracing::AsyncEnd, QStringLiteral(
"3D" ), QStringLiteral(
"DEM" ), jobData.tileId.text() );
308 QByteArray data = jobData.future.result();
309 emit heightMapReady( jobData.jobId, data );