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->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> 
  156  , mClonedProvider( dtm ? qgis::down_cast<
QgsRasterDataProvider *>( dtm->dataProvider()->clone() ) : nullptr )
 
  157  , mTilingScheme( tilingScheme )
 
  158  , 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 )
 
  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)
 
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 ...
 
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.
 
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 xMinimum() const
Returns the x minimum value (left side of rectangle).
 
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
 
double width() const
Returns the width of the rectangle.
 
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.
 
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.