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 )
63 QgsDemHeightMapGenerator *heightMapGenerator =
nullptr;
80 connect( heightMapGenerator, &QgsDemHeightMapGenerator::heightMapReady,
this, &QgsDemTerrainTileLoader::onHeightMapReady );
81 mHeightMapJobId = heightMapGenerator->render( node->tileId() );
82 mResolution = heightMapGenerator->resolution();
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 ) ) );
128 entity->setEnabled(
false );
129 entity->setParent( parent );
133 void 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>
157 , mTilingScheme( tilingScheme )
158 , mResolution( resolution )
161 , mTransformContext( transformContext )
165 QgsDemHeightMapGenerator::~QgsDemHeightMapGenerator()
167 delete mClonedProvider;
173 QgsEventTracing::ScopedEvent e( QStringLiteral(
"3D" ), QStringLiteral(
"DEM" ) );
177 std::unique_ptr<QgsRasterProjector> projector;
178 if ( provider->
crs() != destCrs )
183 input = projector.get();
185 std::unique_ptr< QgsRasterBlock > block( input->
block( 1, extent, res, res ) );
191 data = block->data();
194 if ( block->hasNoData() )
197 float *floatData =
reinterpret_cast<float *
>( data.data() );
198 Q_ASSERT( data.count() %
sizeof(
float ) == 0 );
199 int count = data.count() /
sizeof( float );
200 for (
int i = 0; i < count; ++i )
202 if ( block->isNoData( i ) )
203 floatData[i] = std::numeric_limits<float>::quiet_NaN();
212 return downloader->
getHeightMap( extent, res, destCrs, context );
215 int QgsDemHeightMapGenerator::render(
const QgsChunkNodeId &nodeId )
217 QgsEventTracing::addEvent( QgsEventTracing::AsyncBegin, QStringLiteral(
"3D" ), QStringLiteral(
"DEM" ), nodeId.text() );
220 QgsRectangle extent = mTilingScheme.tileToExtent( nodeId );
221 float mapUnitsPerPixel = extent.
width() / mResolution;
222 extent.
grow( mapUnitsPerPixel / 2 );
224 QgsRectangle fullExtent = mTilingScheme.tileToExtent( 0, 0, 0 );
228 jd.jobId = ++mLastJobId;
232 QFutureWatcher<QByteArray> *fw =
new QFutureWatcher<QByteArray>(
nullptr );
233 connect( fw, &QFutureWatcher<QByteArray>::finished,
this, &QgsDemHeightMapGenerator::onFutureFinished );
234 connect( fw, &QFutureWatcher<QByteArray>::finished, fw, &QObject::deleteLater );
237 jd.future = QtConcurrent::run( _readDtmData, mClonedProvider, extent, mResolution, mTilingScheme.crs() );
239 jd.future = QtConcurrent::run( _readOnlineDtm, mDownloader.get(), extent, mResolution, mTilingScheme.crs(), mTransformContext );
241 fw->setFuture( jd.future );
243 mJobs.insert( fw, jd );
248 void QgsDemHeightMapGenerator::waitForFinished()
250 for ( QFutureWatcher<QByteArray> *fw : mJobs.keys() )
252 disconnect( fw, &QFutureWatcher<QByteArray>::finished,
this, &QgsDemHeightMapGenerator::onFutureFinished );
253 disconnect( fw, &QFutureWatcher<QByteArray>::finished, fw, &QObject::deleteLater );
255 QVector<QFutureWatcher<QByteArray>*> toBeDeleted;
256 for ( QFutureWatcher<QByteArray> *fw : mJobs.keys() )
258 fw->waitForFinished();
259 JobData jobData = mJobs.value( fw );
260 toBeDeleted.push_back( fw );
262 QByteArray data = jobData.future.result();
263 emit heightMapReady( jobData.jobId, data );
266 for ( QFutureWatcher<QByteArray> *fw : toBeDeleted )
273 void QgsDemHeightMapGenerator::lazyLoadDtmCoarseData(
int res,
const QgsRectangle &rect )
275 QMutexLocker locker( &mLazyLoadDtmCoarseDataMutex );
276 if ( mDtmCoarseData.isEmpty() )
278 std::unique_ptr< QgsRasterBlock > block( mDtm->dataProvider()->block( 1, rect, res, res ) );
280 mDtmCoarseData = block->data();
281 mDtmCoarseData.detach();
285 float QgsDemHeightMapGenerator::heightAt(
double x,
double y )
293 lazyLoadDtmCoarseData( res, rect );
295 int cellX = ( int )( ( x - rect.
xMinimum() ) / rect.
width() * res + .5f );
296 int cellY = ( int )( ( rect.
yMaximum() - y ) / rect.
height() * res + .5f );
297 cellX = std::clamp( cellX, 0, res - 1 );
298 cellY = std::clamp( cellY, 0, res - 1 );
300 const float *data = (
const float * ) mDtmCoarseData.constData();
301 return data[cellX + cellY * res];
304 void QgsDemHeightMapGenerator::onFutureFinished()
306 QFutureWatcher<QByteArray> *fw =
static_cast<QFutureWatcher<QByteArray>*
>( sender() );
308 Q_ASSERT( mJobs.contains( fw ) );
309 JobData jobData = mJobs.value( fw );
314 QgsEventTracing::addEvent( QgsEventTracing::AsyncEnd, QStringLiteral(
"3D" ), QStringLiteral(
"DEM" ), jobData.tileId.text() );
316 QByteArray data = jobData.future.result();
317 emit heightMapReady( jobData.jobId, data );
@ Float32
Thirty two bit floating point (float)
QgsTerrainGenerator * terrainGenerator() const
Returns terrain generator. It takes care of producing terrain tiles from the input data.
double terrainVerticalScale() const
Returns vertical scale (exaggeration) of terrain.
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.
const QgsTilingScheme & tilingScheme() const
Returns tiling scheme of the terrain.
virtual Type type() const =0
What texture generator implementation is this.
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.