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 )
62 QgsDemHeightMapGenerator *heightMapGenerator =
nullptr;
79 connect( heightMapGenerator, &QgsDemHeightMapGenerator::heightMapReady,
this, &QgsDemTerrainTileLoader::onHeightMapReady );
80 mHeightMapJobId = heightMapGenerator->render( node->tileId() );
81 mResolution = heightMapGenerator->resolution();
84 Qt3DCore::QEntity *QgsDemTerrainTileLoader::createEntity( Qt3DCore::QEntity *parent )
87 _heightMapMinMax( mHeightMap, zMin, zMax );
89 if ( std::isnan( zMin ) || std::isnan( zMax ) )
96 QgsChunkNodeId nodeId = mNode->tileId();
100 double side = extent.
width();
101 double half = side / 2;
104 QgsTerrainTileEntity *entity =
new QgsTerrainTileEntity( nodeId );
108 Qt3DRender::QGeometryRenderer *mesh =
new Qt3DRender::QGeometryRenderer;
109 mesh->setGeometry(
new DemTerrainTileGeometry( mResolution, side, map.
terrainVerticalScale(), mSkirtHeight, mHeightMap, mesh ) );
110 entity->addComponent( mesh );
118 Qt3DCore::QTransform *transform =
nullptr;
119 transform =
new Qt3DCore::QTransform();
120 entity->addComponent( transform );
122 transform->setScale( side );
123 transform->setTranslation( QVector3D( x0 + half, 0, - ( y0 + half ) ) );
127 entity->setEnabled(
false );
128 entity->setParent( parent );
132 void QgsDemTerrainTileLoader::onHeightMapReady(
int jobId,
const QByteArray &heightMap )
134 if ( mHeightMapJobId == jobId )
136 this->mHeightMap = heightMap;
137 mHeightMapJobId = -1;
149 #include <QtConcurrent/QtConcurrentRun>
150 #include <QFutureWatcher>
156 , mTilingScheme( tilingScheme )
157 , mResolution( resolution )
160 , mTransformContext( transformContext )
164 QgsDemHeightMapGenerator::~QgsDemHeightMapGenerator()
166 delete mClonedProvider;
172 QgsEventTracing::ScopedEvent e( QStringLiteral(
"3D" ), QStringLiteral(
"DEM" ) );
176 std::unique_ptr<QgsRasterProjector> projector;
177 if ( provider->
crs() != destCrs )
182 input = projector.get();
184 std::unique_ptr< QgsRasterBlock > block( input->
block( 1, extent, res, res ) );
190 data = block->data();
193 if ( block->hasNoData() )
196 float *floatData =
reinterpret_cast<float *
>( data.data() );
197 Q_ASSERT( data.count() %
sizeof(
float ) == 0 );
198 int count = data.count() /
sizeof( float );
199 for (
int i = 0; i < count; ++i )
201 if ( block->isNoData( i ) )
202 floatData[i] = std::numeric_limits<float>::quiet_NaN();
211 return downloader->
getHeightMap( extent, res, destCrs, context );
214 int QgsDemHeightMapGenerator::render(
const QgsChunkNodeId &nodeId )
216 QgsEventTracing::addEvent( QgsEventTracing::AsyncBegin, QStringLiteral(
"3D" ), QStringLiteral(
"DEM" ), nodeId.text() );
219 QgsRectangle extent = mTilingScheme.tileToExtent( nodeId );
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(), mTransformContext );
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 = std::clamp( cellX, 0, res - 1 );
297 cellY = std::clamp( cellY, 0, 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 );
@ 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 * > terrainLayers() const
Returns the list of map layers to be rendered as a texture of the terrain.
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.